Compare commits
98 Commits
07b595c45a
...
tp45-schem
| Author | SHA1 | Date | |
|---|---|---|---|
| b91bac1265 | |||
| b509a2325e | |||
| 4d273117b6 | |||
| 92bd4aac85 | |||
| 974ecbc855 | |||
| 139d7c029d | |||
| b52d6aa49e | |||
| 8c6cb84dd1 | |||
| e87619e89b | |||
| 177ba100eb | |||
| bce40d182d | |||
| 1d86983f63 | |||
| 27effe937e | |||
| 2cf32bde14 | |||
| 1f19a3b4a9 | |||
| 3e727f1b3b | |||
| 037d2bd22c | |||
| 0e6742f1d7 | |||
| b7d82fe300 | |||
| 13a996c6d9 | |||
| 2a21afe74a | |||
| 9fdcab58ad | |||
| 441e97ed84 | |||
| 89eb9dc3ac | |||
| 4e80ffac28 | |||
| 548d73d84a | |||
| 3eb03ae7b4 | |||
| 3a97bbc41c | |||
| 7d237577e1 | |||
| 777c4e0984 | |||
| 425070ce11 | |||
| ae87e9f511 | |||
| f941731ac4 | |||
| 3877c23505 | |||
| 123a0fefdf | |||
| aebf3776ac | |||
| b6f2129b41 | |||
| 82cd8098ef | |||
| 614025eafb | |||
| 677dade184 | |||
| b20edd6b62 | |||
| a38ed0fe1d | |||
| 5a29c482bd | |||
| 4eba3673b7 | |||
| 2b856d3d42 | |||
| f1d565bc88 | |||
| 43c592e5d4 | |||
| 33b18a08db | |||
| 799b693ffa | |||
| 4aeced30b5 | |||
| 89d94dbd45 | |||
| 6f60e03679 | |||
| 2f4866ca9a | |||
| 8e41268b4b | |||
| b915d02a82 | |||
| efb03979a0 | |||
| 268fd859ab | |||
| 7a20a1db4f | |||
| bca8f6e27e | |||
| d63dc7bb78 | |||
| b200401c5a | |||
| 384f356290 | |||
| e96fca5a52 | |||
| 1625c30fd7 | |||
| 83fd69ef8d | |||
| 56746cb24d | |||
| a0ba87d555 | |||
| 674e5fff0d | |||
| c024f85579 | |||
| 9caf145341 | |||
| 7edf01b021 | |||
| f507c50623 | |||
| f8982df200 | |||
| c23628f62f | |||
| b75336c9db | |||
| 70afb165b7 | |||
| efb0f44fa7 | |||
| ac3a125c82 | |||
| 45e4540cf5 | |||
| e8f58b0e5e | |||
| 05a3fd957f | |||
| bf6d3605b7 | |||
| 8aae0f424f | |||
| 72db50fce2 | |||
| cce88bdcba | |||
| c7e173ec5a | |||
| 1b413b41f3 | |||
| e376651247 | |||
| fde9f1535a | |||
| ab01934c2d | |||
| 46e6a54ecb | |||
| 403b4528b2 | |||
| 20671aefd4 | |||
| c543819a90 | |||
| 012397f194 | |||
| faa4308223 | |||
| 155dbc08f2 | |||
| 284df839db |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,10 +1,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.secrets
|
.secrets
|
||||||
|
|
||||||
# Per-Projekt-MCP-Configs: können Tokens/URLs enthalten, projekt-spezifisch, nicht ins Repo
|
|
||||||
.mcp.json
|
|
||||||
|
|
||||||
|
|
||||||
# Python bytecode
|
# Python bytecode
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
@@ -25,3 +21,4 @@ caddy/caddy.env
|
|||||||
|
|
||||||
# Config-Backups, die Sessions vor destruktiven Edits anlegen (2026-08-30)
|
# Config-Backups, die Sessions vor destruktiven Edits anlegen (2026-08-30)
|
||||||
*.bak-*
|
*.bak-*
|
||||||
|
docs/macmini-vlan20/stacks
|
||||||
|
|||||||
67
.mcp.json
Normal file
67
.mcp.json
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"home-assistant": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "${MCP_HOME_ASSISTANT_URL}"
|
||||||
|
},
|
||||||
|
"mikromcp": {
|
||||||
|
"type": "stdio",
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"mikromcp",
|
||||||
|
"serve"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"MIKROMCP_CONFIG_PATH": "/Users/christophemsenhuber/dev/bf-infra/mcp/mikromcp/config/routers.yaml",
|
||||||
|
"MIKROMCP_DEFAULT_ROUTER": "ap1",
|
||||||
|
"ROUTER_AP1_USER": "admin",
|
||||||
|
"ROUTER_AP1_PASS": "${MCP_MIKROMCP_ROUTER_AP1_PASS}",
|
||||||
|
"ROUTER_AP2_USER": "admin",
|
||||||
|
"ROUTER_AP2_PASS": "${MCP_MIKROMCP_ROUTER_AP2_PASS}",
|
||||||
|
"CRS326_CORE_USER": "admin",
|
||||||
|
"CRS326_CORE_PASS": "${MCP_MIKROMCP_CRS326_CORE_PASS}",
|
||||||
|
"CRS326_BRANCH_USER": "admin",
|
||||||
|
"CRS326_BRANCH_PASS": "${MCP_MIKROMCP_CRS326_BRANCH_PASS}",
|
||||||
|
"ROUTER_AP3_USER": "admin",
|
||||||
|
"ROUTER_AP3_PASS": "${MCP_MIKROMCP_ROUTER_AP3_PASS}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hostinger-api": {
|
||||||
|
"alwaysLoad": true,
|
||||||
|
"type": "stdio",
|
||||||
|
"command": "hostinger-api-mcp",
|
||||||
|
"args": [],
|
||||||
|
"env": {
|
||||||
|
"HOSTINGER_API_TOKEN": "${MCP_HOSTINGER_API_TOKEN}"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cloudflare": {
|
||||||
|
"alwaysLoad": true,
|
||||||
|
"type": "http",
|
||||||
|
"url": "https://mcp.cloudflare.com/mcp"
|
||||||
|
},
|
||||||
|
"ollama": {
|
||||||
|
"type": "stdio",
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"ollama-mcp"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"OLLAMA_HOST": "http://127.0.0.1:11434"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pve-mcp": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "http://10.40.20.173:3000/"
|
||||||
|
},
|
||||||
|
"n8n-mcp": {
|
||||||
|
"type": "http",
|
||||||
|
"url": "http://10.40.20.167:3001/mcp",
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer ${N8N_MCP_BEARER}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
103
CLAUDE.md
103
CLAUDE.md
@@ -6,33 +6,70 @@ DNS/DHCP-Ablösung, WLAN via **CAPsMAN auf dem Core** (TP28, 2026-08-17 — der
|
|||||||
|
|
||||||
## Pflichtlektüre pro Sitzung (Reihenfolge)
|
## Pflichtlektüre pro Sitzung (Reihenfolge)
|
||||||
|
|
||||||
1. **`docs/teilprojekte.md`** (Quick-Index-Block) — kanonischer Stand, Scope, Abhängigkeiten, **offene** TPs. **1 Read reicht.**
|
0. 🔴 **TP45 = aktuell dringendster Posten (2026-09-21, Gitea #71):** interne `*.bf.emsenhuber.net`
|
||||||
|
lösen im Browser **nicht** auf (AGH wertet Rewrites im **Filter-Pfad** aus → für unfiltrierte
|
||||||
|
Clients übersprungen → Fallback auf den Core, der nur 10 von 26 Namen hält), und `ds-bf` ist
|
||||||
|
**öffentlich kaputt** (einziger Ingress-Eintrag, der Caddy umgeht → TP5-Drop). Reihenfolge:
|
||||||
|
VLAN20 ins TP32-Schema → VLAN30-Pool-Grenze → DNS/FQDN. Ausführlich: `### TP45`.
|
||||||
|
**`### TP46`** = CF-/Tunnel-SSoT + Recovery (eigene Baustelle).
|
||||||
|
1. **Status/Index = Gitea-Board** (seit TP36-Cutover 2026-09-07): [`admin/bf-infra` · Issues](https://gitea.emsenhuber.net/admin/bf-infra/issues) — TP = Issue, Status = Label `status:*`, Gruppierung = `thema:*`. Für den **Detail/Kontext** eines TP dann den `### TPnn`-Prosa-Abschnitt in **`docs/teilprojekte.md`** (aktiv) bzw. **`docs/teilprojekte-archiv.md`** lesen. *(Die frühere „Quick-Index-Block, 1 Read reicht"-Zeile ist überholt — Index/Status stehen nicht mehr in der Datei.)*
|
||||||
2. **`docs/handoff-*.md`** (neueste) — wo die letzte Session aufgehört hat.
|
2. **`docs/handoff-*.md`** (neueste) — wo die letzte Session aufgehört hat.
|
||||||
3. Bei Live-Writes oder Inventur: **`docs/cf-tunnel-mapping.md`**, **`docs/teilprojekte-archiv.md`**.
|
3. Bei Live-Writes oder Inventur: **`docs/cf-tunnel-mapping.md`**, **`docs/teilprojekte-archiv.md`**.
|
||||||
4. ✅ **TP16.3 §5 durch (2026-09-01):** [`docs/handoff-20260901-tp16.3-paragraph5-flip-durch.md`](docs/handoff-20260901-tp16.3-paragraph5-flip-durch.md)
|
4. ✅ **TP16.3 §5 durch (2026-09-01):** [`docs/handoff-20260901-tp16.3-paragraph5-flip-durch.md`](docs/handoff-20260901-tp16.3-paragraph5-flip-durch.md)
|
||||||
— alle vier oeffentlichen Namen laufen ueber `bf-public`/Caddy; Alttunnel bleiben als Rueckfallebene
|
— alle vier oeffentlichen Namen laufen ueber `bf-public`/Caddy; Alttunnel bleiben als Rueckfallebene
|
||||||
(§6 offen). Enthaelt den 10-Sekunden-DNS-Fund und drei Messfallen.
|
(§6 offen). Enthaelt den 10-Sekunden-DNS-Fund und drei Messfallen.
|
||||||
5. 🔴 **WLAN/Fund E — HEBEL 1 WIDERLEGT UND ZURUECKGEBAUT, Messung laeuft:**
|
5. ✅ **WLAN/Fund E — GESCHLOSSEN 2026-09-02: 7.24.1 war die Ursache, nicht `ft-over-ds`:**
|
||||||
[`docs/handoff-20260901-fund-e-hebel1-zurueckgebaut.md`](docs/handoff-20260901-fund-e-hebel1-zurueckgebaut.md)
|
[`docs/handoff-20260902-tp25-fund-e-messung.md`](docs/handoff-20260902-tp25-fund-e-messung.md)
|
||||||
— `ft-over-ds=no` lief 6,4 h **allein** und brachte 124 `reauthenticating` + zwei erlebte
|
— die Abschlussmessung ergab am 09-02 nur **4 `reauthenticating` in 8,6 h** (alle iPhone `F2:62`,
|
||||||
Ausfaelle; der Einbruch auf 0/h faellt exakt auf das **TP27-Upgrade (7.24.1)** um 14:36.
|
isolierte Einzel, 0 Ausfaelle), Verlauf **218 → 40 → 4**; Vorbedingung geprueft (Core-Uptime 1d18h,
|
||||||
Hebel 1 ist am **2026-09-01** zurueckgebaut (`ft-over-ds=yes`), Transaktion **geschlossen**,
|
`caps joined=0`). Hebel 1 (`ft-over-ds=no`) war widerlegt (6,4 h allein → 124× + 2 Ausfaelle) und ist
|
||||||
**Compliance 140/140**. ⏳ **Offene Aufgabe: eine Messung am 2026-09-02 frueh** — bleibt es
|
am 09-01 zurueckgebaut; Hebel 2–4 entfallen, **kein Geraete-Write** (Soll stand: `ft=yes ft-over-ds=yes`,
|
||||||
ruhig, war 7.24.1 die Ursache. Vorstufen:
|
**Compliance 140/140**). **Am 2026-09-04 hat Christoph `ft=no` auf `sec-main` als Dauerzustand
|
||||||
|
entschieden** (Hebel 2 doch gezogen: Template + bf-golden-config + Nautobot-Sync, `wifi-security` gruen).
|
||||||
|
⚠️ **Rueckgenommen am 2026-09-14 (Terrasse-In-situ-Session): `ft=yes ft-over-ds=yes` zurueck** — mit
|
||||||
|
`ft=no` war jeder Roam eine Voll-Reassoziation, darin starben 2 Anrufe; Rueckkehrstand = Fund-E-Known-Good
|
||||||
|
von 09-02 (ruhig auf 7.24.1). Weg: Template + `push-gc-template.py` + `gc-configplan.py
|
||||||
|
--refresh-compliance` + `gc-manual-plan.py --deploy` (Plan `b1135f3e`, Completed) → **140/140, 0 Drifts**;
|
||||||
|
CAP-Abnahme: **keine** Inline-Overrides auf ap1/ap2/ap3. Commands:
|
||||||
|
`docs/transaktionen/TP25-ft-yes-secmain.commands.rsc`, s. Handoff 2026-09-14.
|
||||||
|
🔴 **Nachtrag 09-04: `ft=no` war NICHT uniform.** ap3 trug einen Werks-Inline `security.ft=yes` auf
|
||||||
|
`wifi1/wifi2` (Bring-up unset `!ft-over-ds`, aber nicht `!ft`; Abnahme zählt nur benannte
|
||||||
|
security-Objekte → Inline-Wert unsichtbar). Gemischtes FT-ESS → Strong-Signal-Abbrüche (−43/−45 dBm)
|
||||||
|
**nur auf ap3** bei 3/3 Strichen. Fix: `!security.ft` auf ap3 (uniform `ft=no`), Quelle
|
||||||
|
`tp28-ap3-phase2-cap.rsc` korrigiert. **Merkregel: nach jeder `sec-main`-FT-Änderung jeden CAP einzeln
|
||||||
|
`get wifiN security.ft` prüfen — CAP-lokale Inline-Overrides sind compliance-unsichtbar.**
|
||||||
|
Der TP25-Kanalplan ist am 09-04 committed (DFS-Frequenzlisten + ap1-Provisioning, **140/140, 0 Drifts**).
|
||||||
|
Offen in TP25 bleibt nur **ap2 unterperformt** (2 m/−80 dBm). Vorstufen:
|
||||||
[Befund](docs/handoff-20260831-tp27-upgrade-und-hebel1-befund.md) ·
|
[Befund](docs/handoff-20260831-tp27-upgrade-und-hebel1-befund.md) ·
|
||||||
[Eingriff](docs/handoff-20260831-fund-e-hebel1-gezogen.md).
|
[Eingriff](docs/handoff-20260831-fund-e-hebel1-gezogen.md) ·
|
||||||
|
[Rueckbau](docs/handoff-20260901-fund-e-hebel1-zurueckgebaut.md).
|
||||||
6. ap3-Bring-up 2026-08-30: [`docs/handoff-20260830-ap3.md`](docs/handoff-20260830-ap3.md) — sechs Funde zu Werks-MikroTiks, CAPsMAN und Golden-Config-Lücken.
|
6. ap3-Bring-up 2026-08-30: [`docs/handoff-20260830-ap3.md`](docs/handoff-20260830-ap3.md) — sechs Funde zu Werks-MikroTiks, CAPsMAN und Golden-Config-Lücken.
|
||||||
7. Inventur-Befund 2026-08-27: [`docs/handoff-20260827-inventur-und-architektur.md`](docs/handoff-20260827-inventur-und-architektur.md) — Master-Struktur, Asymmetrie, Drift.
|
7. Inventur-Befund 2026-08-27: [`docs/handoff-20260827-inventur-und-architektur.md`](docs/handoff-20260827-inventur-und-architektur.md) — Master-Struktur, Asymmetrie, Drift.
|
||||||
|
|
||||||
|
## SSoT & Branch-Konvention (TP36-Cutover 2026-09-07)
|
||||||
|
|
||||||
|
- **SSoT-Dreiteilung:** **Index/Status = Gitea-Issues** (`admin/bf-infra`, Labels `status:*`/`thema:*`) ·
|
||||||
|
**Prosa/Detail = Repo** (`docs/teilprojekte.md` aktiv + `teilprojekte-archiv.md`, `### TPnn`-Abschnitte) ·
|
||||||
|
**Live-Gerätedaten = Nautobot** (Compliance-Zahl, RouterOS-Version …). Ein Status ändert sich **in Gitea**,
|
||||||
|
nicht mehr in einer Repo-Datei. Sync-/Abgleich-Werkzeug fürs Archiv: `bin/gitea-tp-sync.py` (Dry-Run default,
|
||||||
|
Token aus `~/.hermes/.env`, **nie ausgeben**).
|
||||||
|
- **Branch-Konvention:** Branch `tpNN-kurz` **jederzeit** mitten in der Session möglich (`git switch -c`,
|
||||||
|
uncommittete Arbeit wandert mit); für Multi-Session-TPs der Normalfall. **Merge nach `main`, sobald der
|
||||||
|
Code/Config des TP steht.** Weil Steuerungs-Updates nach **Gitea** gehen (nicht in `teilprojekte.md`),
|
||||||
|
kollidieren TP-Branches nicht mehr auf der Steuerungs-Doku — nur noch der eigene `### TPnn`-Prosa-Abschnitt
|
||||||
|
wird angefasst.
|
||||||
|
- **TP anlegen/schließen:** neues aktives TP = **direkt als Gitea-Issue** (Labels setzen), Prosa-Abschnitt
|
||||||
|
im Repo dazu; abschließen = Issue `closed`, Prosa wandert bei Bedarf ins Archiv. **Keine Handtabelle mehr.**
|
||||||
|
|
||||||
## Was du sofort wissen musst (Inventur 2026-08-27)
|
## Was du sofort wissen musst (Inventur 2026-08-27)
|
||||||
|
|
||||||
- **`bf-infra`** ist aktiver Master **mit Gitea-Remote**: `origin` = `https://gitea.emsenhuber.net/admin/bf-infra.git` (live verifiziert 2026-08-30). ⚠️ **Die frühere Zeile „lokal, kein Gitea-Remote → verloren bei Festplattencrash" ist überholt** — sie stammt aus der Inventur vom 2026-08-27 und war damals richtig; das Remote existiert seit spätestens 2026-08-28 (`origin/main` = `b917691`). **Die Backup-Lücke ist damit keine Struktur-, sondern eine Disziplinfrage: `git push`.** Am 2026-08-30 lagen vier Commits nur lokal. 🔴 **Das Remote ist nur so viel wert wie der letzte Push** — der Backup-Guard (`~/dev/.bin/backup-guard.sh`, TP35 Schicht 5) bricht Commits ab, solange kein Backup markiert ist; **das ist die Erinnerung ans Pushen, kein Hindernis.** Nicht mit `--no-verify` umgehen. 🔴 **Merkregel:** diese Zeile hat eine Session dazu gebracht, „kein Remote" zu behaupten, statt `git remote -v` zu tippen — vor jeder Aussage über Remotes/Backups **live prüfen**.
|
- **`bf-infra`** ist aktiver Master **mit Gitea-Remote**: `origin` = `https://gitea.emsenhuber.net/admin/bf-infra.git` (live verifiziert 2026-08-30). ⚠️ **Die frühere Zeile „lokal, kein Gitea-Remote → verloren bei Festplattencrash" ist überholt** — sie stammt aus der Inventur vom 2026-08-27 und war damals richtig; das Remote existiert seit spätestens 2026-08-28 (`origin/main` = `b917691`). **Die Backup-Lücke ist damit keine Struktur-, sondern eine Disziplinfrage: `git push`.** Am 2026-08-30 lagen vier Commits nur lokal. 🔴 **Das Remote ist nur so viel wert wie der letzte Push** — der Backup-Guard (`~/dev/.bin/backup-guard.sh`, TP35 Schicht 5) bricht Commits ab, solange kein Backup markiert ist; **das ist die Erinnerung ans Pushen, kein Hindernis.** Nicht mit `--no-verify` umgehen. 🔴 **Merkregel:** diese Zeile hat eine Session dazu gebracht, „kein Remote" zu behaupten, statt `git remote -v` zu tippen — vor jeder Aussage über Remotes/Backups **live prüfen**.
|
||||||
- **`admin/bf-network-r6`** (Gitea) ist Archiv, letzte aktive Commits 2026-08-25 → read-only-Header.
|
- **`admin/bf-network-r6`** (Gitea) ist Archiv, letzte aktive Commits 2026-08-25 → read-only-Header.
|
||||||
- **`admin/bf-golden-config`** (Gitea) ist Bot-Master (Nautobot 2×/Tag), nicht manuell.
|
- **`admin/bf-golden-config`** (Gitea) ist Bot-Master, nicht manuell. 🔴 **Korrigiert 2026-09-12:** die frühere Angabe „2×/Tag" war nie real — es gab **keinen** Scheduler, Backup/Intended/Compliance liefen nur manuell aus Sessions (zuletzt 09-04, 8 Tage Stillstand). Jetzt **täglich 03:30 UTC** per ScheduledJob **„GC Backup+Compliance – taeglich"** (Nautobot-Job *Execute All Golden Configuration Jobs - Multiple Device*, Scope `platform=RouterOS`, Reihenfolge Backup→Intended→Compliance). Stand prüfen: `backup_last_success_date` am Gerät bzw. `scheduled-jobs` in Nautobot.
|
||||||
- **`admin/blaueblueten-n8n`** (Gitea) ist Master für terpene-Repo.
|
- **`admin/blaueblueten-n8n`** (Gitea) ist Master für terpene-Repo.
|
||||||
- **`inventory/` ist tot** (Pipeline seit 2025-06 stale). Live-Daten aus MikroMCP/CF/Portainer.
|
- **`inventory/` ist tot** (Pipeline seit 2025-06 stale). Live-Daten aus MikroMCP/CF/Portainer.
|
||||||
- **`mcp/INVENTORY.md`** ist BF-Netz-SSoT; `~/.claude.json`/`opencode.jsonc` sind globalere Werkzeugkästen.
|
- **`mcp/INVENTORY.md`** ist BF-Netz-SSoT; `~/.claude.json`/`opencode.jsonc` sind globalere Werkzeugkästen.
|
||||||
- **terpene.teilzeitladen.xyz**: Worker läuft auf Mac mini (`10.40.20.167:8890`), **kein Caddy-Block, kein CF-Tunnel** — Backfill via DB-Migration blockiert (Handoff 2026-08-27).
|
- **Terpenfinder = `420.teilzeitladen.xyz`** (CF-Worker, Repo `admin/blaueblueten-n8n`, hinter CF Access), **kein Caddy, kein Mac mini im Pfad**. `terpene.*` **existiert nicht und soll es nicht** (live geprüft 2026-09-10). Einziger terpene-benannter CF-Gegenstand: der **interne** Tunnel `terpene-db` (`f2d2fc8a`, ohne öffentlichen Hostname, Hyperdrive→VPC→Postgres, s. `docs/cf-tunnel-mapping.md` §17). ⚠️ **Grenze:** blaueblüten ist ein eigenes Projekt — nicht anfassen. *(Die frühere „terpene.teilzeitladen.xyz:8890 via Tunnel"-Zeile war veraltet — bf-mm ist gelöscht.)*
|
||||||
- **shelly-mcp / portainer-mcp bewusst NICHT als MCP-Client** (User-Direktive 2026-08-28): shelly-mcp-Container auf DS923 wird via **Portainer-API** (Skript-Aufrufe, `mcp/portainer/.env` als Token-Quelle) deployt, nicht via MCP-Protokoll. Inventar-Eintrag spiegelt das. `mcp-host-policy.md` ist SSoT für die Host-/IP-Konvention.
|
- **shelly-mcp / portainer-mcp bewusst NICHT als MCP-Client** (User-Direktive 2026-08-28): shelly-mcp-Container auf DS923 wird via **Portainer-API** (Skript-Aufrufe, `mcp/portainer/.env` als Token-Quelle) deployt, nicht via MCP-Protokoll. Inventar-Eintrag spiegelt das. `mcp-host-policy.md` ist SSoT für die Host-/IP-Konvention.
|
||||||
- **Mac mini `10.40.20.167`** ist der **n8n-Host** (n8n `:5678`, n8n-redis `:6379`, n8n-mcp `:3001`) — **NICHT** der zentrale MCP-Host. Performance-Überlegung: MCP-Clients würden auf dem macmini (User-Workstation) laufen und die Box zusätzlich belasten; deshalb liegt der MCP-Host auf der **DS923** (`nas-bf`, Portainer Endpoint 14). gitea-mcp läuft aktuell noch auf dem macmini (historisch, eigene Container-Definition dort) — gehört **bei Gelegenheit** auf die DS923 umgezogen (separater Punkt, **nicht** Teil dieser Session). Container-Definitionen für n8n leben auf dem macmini (Docker Desktop); FQDN→Backend-Mapping im `caddy/Caddyfile`.
|
- **Mac mini `10.40.20.167`** ist der **n8n-Host** (n8n `:5678`, n8n-redis `:6379`, n8n-mcp `:3001`) — **NICHT** der zentrale MCP-Host. Performance-Überlegung: MCP-Clients würden auf dem macmini (User-Workstation) laufen und die Box zusätzlich belasten; deshalb liegt der MCP-Host auf der **DS923** (`nas-bf`, Portainer Endpoint 14). gitea-mcp läuft aktuell noch auf dem macmini (historisch, eigene Container-Definition dort) — gehört **bei Gelegenheit** auf die DS923 umgezogen (separater Punkt, **nicht** Teil dieser Session). Container-Definitionen für n8n leben auf dem macmini (Docker Desktop); FQDN→Backend-Mapping im `caddy/Caddyfile`.
|
||||||
|
|
||||||
@@ -40,12 +77,15 @@ DNS/DHCP-Ablösung, WLAN via **CAPsMAN auf dem Core** (TP28, 2026-08-17 — der
|
|||||||
|
|
||||||
Wenn während einer Session ein neues Teilprojekt, eine Abhängigkeit oder ein Querschnittsthema sichtbar wird, das bisher in keinem TP abgebildet ist:
|
Wenn während einer Session ein neues Teilprojekt, eine Abhängigkeit oder ein Querschnittsthema sichtbar wird, das bisher in keinem TP abgebildet ist:
|
||||||
- Vorschlagen und mit dir abstimmen (nicht eigenmächtig anlegen).
|
- Vorschlagen und mit dir abstimmen (nicht eigenmächtig anlegen).
|
||||||
- Auf dein Okay hin anlegen: Quick-Index-Zeile + `### TP<n>`-Abschnitt mit Status `🔜`, Scope,
|
- Auf dein Okay hin anlegen: **Gitea-Issue** `TPnn — <Kurztitel>` mit Labels `status:🔜`(todo) +
|
||||||
Abhängigkeiten, Abgrenzung. Nächste freie Nummer vergeben (höchste vergebene + 1; Lücken aus
|
passendem `thema:*` + Body (Anriss + Verweis auf den Prosa-Abschnitt) **und** einen
|
||||||
verworfenen TPs bleiben frei).
|
`### TP<n>`-Prosa-Abschnitt in `docs/teilprojekte.md` (Scope, Abhängigkeiten, Abgrenzung).
|
||||||
- Ist-Stand-Block mitziehen (Datum, betroffene Zeilen).
|
Nächste freie Nummer vergeben (höchste vergebene + 1; Lücken aus verworfenen TPs bleiben frei).
|
||||||
|
- **Status/Reihenfolge/„was lief zuletzt" gehen ins Gitea-Issue** (Label ändern, Kommentar), **nicht**
|
||||||
|
in eine Quick-Index-Tabelle oder einen Ist-Stand-Block — die gibt es seit dem TP36-Cutover (2026-09-07)
|
||||||
|
nicht mehr in der Datei.
|
||||||
|
|
||||||
**Abgeschlossene TPs** stehen seit 2026-08-23 in `docs/teilprojekte-archiv.md` (Volltext, unverändert, mit Inhaltsverzeichnis). **Ein TP wandert erst ins Archiv, wenn nichts mehr daran zu tun ist — und wieder zurück, sobald doch etwas dazukommt.** Ein archiviertes TP ist ein *geschlossenes Ticket, kein versiegeltes*: bei neuer Arbeit an derselben Baustelle wandert der **Volltext zurück** nach `teilprojekte.md` (Zeiger bleibt im Archiv), **kein** Zwillings-TP mit neuer Nummer (Christoph-Direktive 2026-08-30, erster Fall TP28/`ap3`) — und der **Ist-Stand-Block wird bei jeder Änderung mitgezogen**, nicht nur der betroffene TP-Abschnitt.
|
**Abgeschlossene TPs** stehen seit 2026-08-23 in `docs/teilprojekte-archiv.md` (Volltext, unverändert, mit Inhaltsverzeichnis) und ihr Gitea-Issue ist `closed` (Label `status:✅`/`❌`). **Ein TP wandert erst ins Archiv, wenn nichts mehr daran zu tun ist — und wieder zurück, sobald doch etwas dazukommt** (Issue dann `reopen`). Ein archiviertes TP ist ein *geschlossenes Ticket, kein versiegeltes*: bei neuer Arbeit an derselben Baustelle wandert der **Volltext zurück** nach `teilprojekte.md` (Zeiger bleibt im Archiv), **kein** Zwillings-TP mit neuer Nummer (Christoph-Direktive 2026-08-30, erster Fall TP28/`ap3`) — nur noch der betroffene `### TPnn`-Prosa-Abschnitt wird angefasst.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -70,15 +110,15 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`,
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| ~~ALLNET Core-Switch (sw1)~~ | — | ❌ **außer Betrieb (2026-08-08)** — durch `crs326-core` (L3-Router + Core-Switch, Technik UG) ersetzt; physisch abgelöst. (Telnet-Runner `switch/` = nur noch historisch.) |
|
| ~~ALLNET Core-Switch (sw1)~~ | — | ❌ **außer Betrieb (2026-08-08)** — durch `crs326-core` (L3-Router + Core-Switch, Technik UG) ersetzt; physisch abgelöst. (Telnet-Runner `switch/` = nur noch historisch.) |
|
||||||
| ~~Omada Access-Switch (sw2)~~ | — | ❌ **abgebaut 2026-07-23** (TP15 Branch-Tausch) — durch `crs326-branch` in Technik OG ersetzt, physisch entfernt |
|
| ~~Omada Access-Switch (sw2)~~ | — | ❌ **abgebaut 2026-07-23** (TP15 Branch-Tausch) — durch `crs326-branch` in Technik OG ersetzt, physisch entfernt |
|
||||||
| **crs326-branch** (CRS326, Technik OG) | **10.40.99.2** (VLAN99) · Bootstrap Rev4 2026-08-08 | RouterOS **7.24.1** · `admin`/`~/.secrets` (`CRS326_BRANCH_*`) · REST `http://10.40.99.2/rest` · MikroMCP `crs326-branch` ✅ **live** (2026-08-16 verifiziert) · **TP15 ✅: ersetzt sw2** (L2-Switch), volle Portbelegung (ether1 Uplink-Trunk, 2/8 NAS, 3 Drucker, 4 Dock, 5/6 Sonos, 7 macmini, 24 ap2) · sw-id RGZJ-UK0T, MAC 04:F4:1C:B4:15:58 |
|
| **crs326-branch** (CRS326, Technik OG) | **10.40.99.2** (VLAN99) · Bootstrap Rev4 2026-08-08 | RouterOS **7.24.1** · `admin`/`~/.secrets` (`CRS326_BRANCH_*`) · REST `http://10.40.99.2/rest` · MikroMCP `crs326-branch` ✅ **live** (2026-08-16 verifiziert) · **TP15 ✅: ersetzt sw2** (L2-Switch), volle Portbelegung (ether1 Uplink-Trunk, 2/8 NAS, 3 Drucker, 4 Dock, 5/6 Sonos, 7 macmini, 24 ap3) · sw-id RGZJ-UK0T, MAC 04:F4:1C:B4:15:58 |
|
||||||
| **crs326-core** (CRS326, Technik UG) | **10.40.99.1** (VLAN99-SVI = L3-GW; same-subnet-Fallback `10.40.10.1`) · Identity `crs326-core` | RouterOS **7.24.1** · `admin`/`~/.secrets` (`CRS326_CORE_*`) · MikroMCP routerId `crs326-core` ✅ **live** (2026-08-16 verifiziert) · **TP15/TP21.1 ✅ 2026-08-08: PRODUKTIVER L3-Router** (ersetzt ap1) — Routing/DHCP (7 Server + 54 Leases)/DNS (**DoH zum CF-Gateway mit `verify-doh-cert=yes`**, TP23 Stufe 1 ✅ 2026-08-18; `servers=1.1.1.1` nur noch Bootstrap) · **NTP-Server für den Standort**/NAT/WG-Hub (fm+dw live); WAN `vlan100`=Liwest `90.146.33.177` · sw-id 4W04-URHT, MAC 04:F4:1C:B4:17:76 |
|
| **crs326-core** (CRS326, Technik UG) | **10.40.99.1** (VLAN99-SVI = L3-GW; same-subnet-Fallback `10.40.10.1`) · Identity `crs326-core` | RouterOS **7.24.1** · `admin`/`~/.secrets` (`CRS326_CORE_*`) · MikroMCP routerId `crs326-core` ✅ **live** (2026-08-16 verifiziert) · **TP15/TP21.1 ✅ 2026-08-08: PRODUKTIVER L3-Router** (ersetzt ap1) — Routing/DHCP (7 Server + 54 Leases)/DNS (**DoH zum CF-Gateway mit `verify-doh-cert=yes`**, TP23 Stufe 1 ✅ 2026-08-18; `servers=1.1.1.1` nur noch Bootstrap) · **NTP-Server für den Standort**/NAT/WG-Hub (fm+dw live); WAN `vlan100`=Liwest `90.146.33.177` · sw-id 4W04-URHT, MAC 04:F4:1C:B4:17:76 |
|
||||||
| hAP ax² #1 (ap1) | **10.40.99.11** (VLAN99) · Identity `ap1` | RouterOS **7.24.1** · `admin`/`~/.secrets` (`ROUTER_AP1_*`) · REST `http://10.40.99.11/rest` · MikroMCP `ap1` ✅ live · **TP28 ✅ 2026-08-17: reiner CAP** (Manager = `crs326-core`) · 🔴 **FUNKSTILL seit 2026-08-30**: keine Provisioning-Regel am Manager, weil ap3 daneben steht und denselben Kanal 5180 fährt. Gerät bleibt erreichbar. Beim Umzug ans Sideboard → `5260/Ce` (Band B, DFS) und Uplink auf `core ether3` — Uplink `ether1` (PoE-Port, `poe-out=off`) an crs326-core `ether15`; SSIDs/Kanäle kommen vom Manager, ch36/ch1 · MAC D0:EA:11:4D:40:E0 · Config-Referenz `routeros/.secrets/ap1-prov-base.rsc` |
|
| hAP ax² #1 (ap1) | **10.40.99.11** (VLAN99) · Identity `ap1` | RouterOS **7.24.1** · `admin`/`~/.secrets` (`ROUTER_AP1_*`) · REST `http://10.40.99.11/rest` · MikroMCP `ap1` ✅ live · **TP28 ✅ 2026-08-17: reiner CAP** (Manager = `crs326-core`) · ✅ **aktiv am Sideboard** (Wohnzimmer; Uplink `ether1` → core `ether3`, PoE-Port `poe-out=off`) — Kanäle `5260/Ce` (Band B, DFS, Fallback 5280) + `2412`/ch1, vom Manager · MAC D0:EA:11:4D:40:E0 · Config-Referenz `routeros/.secrets/ap1-prov-base.rsc` |
|
||||||
| hAP ax² #2 (ap2) | **10.40.99.12** (VLAN99) · Identity `ap2` | RouterOS **7.24.1** · `admin`/`~/.secrets` (`ROUTER_AP2_*`) · REST `http://10.40.99.12/rest` · MikroMCP `ap2` · **TP28 ✅ 2026-08-17: reiner CAP** — Uplink `ether1` (PoE) an crs326-branch `ether24`; ch44/ch11 (TP25: ch149 war unter ETSI leistungsbegrenzt) · MAC D0:EA:11:4D:40:3A · Config `routeros/.secrets/ap2-prov-base.rsc` |
|
| hAP ax² #2 (ap2) | **10.40.99.12** (VLAN99) · Identity `ap2` | RouterOS **7.24.1** · `admin`/`~/.secrets` (`ROUTER_AP2_*`) · REST `http://10.40.99.12/rest` · MikroMCP `ap2` · **TP28 ✅ 2026-08-17: reiner CAP** — **Technik UG**, Uplink `ether1` (PoE) an core `ether21`; Kanäle `5300/Ce` (Band B, DFS, Fallback 5320) + `2462`/ch11 · MAC D0:EA:11:4D:40:39 (ether1; Bridge/Mgmt `:3A`) · Config `routeros/.secrets/ap2-prov-base.rsc` |
|
||||||
| **cAP ax (ap3)** | **10.40.99.13** (VLAN99) · Identity `ap3` | ✅ **LIVE seit 2026-08-30** (TP28-Nachtrag) · `cAPGi-5HaxD2HaxD`, RouterOS **7.24.1** + RouterBOOT 7.24.1 (**seit 2026-08-31 ist die ganze Flotte auf 7.24.1**, TP27-Angleichung erledigt) · reiner CAP, Manager `crs326-core` · Uplink `ether1` → **core `ether21`** (Trunk) · Kanäle **`5180/ax/Ce/I`** (5 GHz) + **`2437/ax`** (ch6) nach TP25-Plan · 🔴 **nur 2 Ethernet-Ports** (`ether1` PoE-in, `ether2` PoE-out, **kein** Bridge-Port) → `ap.extra_ports=[]` · `device-mode=basic` (scheduler/fetch/romon **erlaubt**, anders als ap1/ap2) · Dateien `routeros/tp28-ap3-phase{1,2}-*.rsc` + `-service-accounts.rsc` + `-werksreste.rsc` |
|
| **cAP ax (ap3)** | **10.40.99.13** (VLAN99) · Identity `ap3` | ✅ **LIVE seit 2026-08-30** (TP28-Nachtrag) · `cAPGi-5HaxD2HaxD`, RouterOS **7.24.1** + RouterBOOT 7.24.1 (**seit 2026-08-31 ist die ganze Flotte auf 7.24.1**, TP27-Angleichung erledigt) · reiner CAP, Manager `crs326-core` · **Technik OG**, Uplink `ether1` → **branch `ether24`** (Trunk) · Kanäle **`5180/ax`** (Band A, non-DFS, 5 GHz) + **`2437`** (ch6) nach TP25-Plan · 🔴 **nur 2 Ethernet-Ports** (`ether1` PoE-in, `ether2` PoE-out, **kein** Bridge-Port) → `ap.extra_ports=[]` · `device-mode=basic` (scheduler/fetch/romon **erlaubt**, anders als ap1/ap2) · Dateien `routeros/tp28-ap3-phase{1,2}-*.rsc` + `-service-accounts.rsc` + `-werksreste.rsc` |
|
||||||
| **🔑 Einheitliches Admin-PW (2026-08-08)** | alle 4 MikroTik (core/branch/ap1/ap2) | user `admin`, gleiches PW (`~/.secrets/credentials.env` → `ROUTER_*_PASS`/`CRS326_*_PASS`; „Überlebensversicherung", User-Vorgabe). Werks-Defaults ap1/ap2 damit obsolet. |
|
| **🔑 Einheitliches Admin-PW (2026-08-08)** | alle 4 MikroTik (core/branch/ap1/ap2) | user `admin`, gleiches PW (`~/.secrets/credentials.env` → `ROUTER_*_PASS`/`CRS326_*_PASS`; „Überlebensversicherung", User-Vorgabe). Werks-Defaults ap1/ap2 damit obsolet. |
|
||||||
| NAS (nas-bf, DS923) | 10.40.20.10 | SSH `adm.christoph@10.40.20.10` (kein PW nötig) · Docker (Portainer endpointId=14) |
|
| NAS (nas-bf, DS923) | 10.40.20.10 | SSH `adm.christoph@10.40.20.10` (kein PW nötig) · Docker (Portainer endpointId=14) |
|
||||||
| platform-pg (LXC 105, pve) | 10.40.20.20 | SSH via pve (`pct exec 105 --`) · **TP20 ✅ 2026-07-16: PG16, DB `nautobot`, Creds `postgres/.secrets`** |
|
| platform-pg (LXC 105, pve) | 10.40.20.20 | SSH via pve (`pct exec 105 --`) · **TP20 ✅ 2026-07-16: PG16, DB `nautobot`, Creds `postgres/.secrets`** |
|
||||||
| Nautobot (Portainer-Stack, NAS) | 10.40.20.30 | Portainer ep14, Stack Id 203 · **TP21 ✅ 2026-07-16: `ghcr.io/nautobot/nautobot:3.1.7`, DB=platform-pg, Creds in `docs/nas-vlan20/stacks/210-nautobot.yml` (gitignored)** · **TP22 ✅ 2026-08-13: Golden-Config-App + Device-Adoption DURCH** — alle 4 MikroTik adoptiert, Deploy-Pipeline scharf (Image `3.1.7-gc9`), **68/68 Compliance-Checks grün / 0 Drifts** (2026-08-16) |
|
| Nautobot (Portainer-Stack, NAS) | **10.40.20.154** (TP45 Schicht A 2026-09-21: `.30/.31/.32/.34` → `.154/.155/.156/.158`) | Portainer ep14, Stack Id 203 · **TP21 ✅ 2026-07-16: `ghcr.io/nautobot/nautobot:3.1.7`, DB=platform-pg, Creds in `docs/nas-vlan20/stacks/210-nautobot.yml` (gitignored)** · **TP22 ✅ 2026-08-13: Golden-Config-App + Device-Adoption DURCH** — alle 4 MikroTik adoptiert, Deploy-Pipeline scharf (Image `3.1.7-gc9`), **68/68 Compliance-Checks grün / 0 Drifts** (2026-08-16) |
|
||||||
| ~~Omada-Controller~~ | — | ❌ **entfernt 2026-07-16** (User hat den Container gelöscht — overkill für 1 Switch auf der Abschussliste, s. TP2.1/TP15) |
|
| ~~Omada-Controller~~ | — | ❌ **entfernt 2026-07-16** (User hat den Container gelöscht — overkill für 1 Switch auf der Abschussliste, s. TP2.1/TP15) |
|
||||||
| ~~omada-mcp~~ | — | ❌ **entfernt 2026-07-16** — mit dem Controller gelöscht. ✅ **Aufräumen erledigt/gegenstandslos (geprüft 2026-08-16):** nicht mehr in `mcpServers` registriert (nur noch als „disabled" in ein paar Projekt-Scopes = harmloser Merkposten), und **kein `omada`-Hostname mehr im CF-Ingress** |
|
| ~~omada-mcp~~ | — | ❌ **entfernt 2026-07-16** — mit dem Controller gelöscht. ✅ **Aufräumen erledigt/gegenstandslos (geprüft 2026-08-16):** nicht mehr in `mcpServers` registriert (nur noch als „disabled" in ein paar Projekt-Scopes = harmloser Merkposten), und **kein `omada`-Hostname mehr im CF-Ingress** |
|
||||||
| pve-mcp | 10.40.20.173 | **StreamableHTTP `http://10.40.20.173:3000/` (Root-Pfad, NICHT /sse)** · NAS-Stack ep14 (=nas-bf `.20.10`, NICHT LXC .126!) id 202 (vlan20-only, TP14.1 2026-07-10) · **mcp-pve v0.6.1 (Upgrade 2026-07-10: Single-Session-Bug 0.4.8 „Server already initialized" gefixt, TP14.2a)** · `~/.claude.json` ✓ |
|
| pve-mcp | 10.40.20.173 | **StreamableHTTP `http://10.40.20.173:3000/` (Root-Pfad, NICHT /sse)** · NAS-Stack ep14 (=nas-bf `.20.10`, NICHT LXC .126!) id 202 (vlan20-only, TP14.1 2026-07-10) · **mcp-pve v0.6.1 (Upgrade 2026-07-10: Single-Session-Bug 0.4.8 „Server already initialized" gefixt, TP14.2a)** · `~/.claude.json` ✓ |
|
||||||
@@ -116,6 +156,25 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`,
|
|||||||
⚠️ **`POST /api/plugins/golden-config/config-plan/` ist `405` — das ViewSet ist read-only.** ConfigPlans
|
⚠️ **`POST /api/plugins/golden-config/config-plan/` ist `405` — das ViewSet ist read-only.** ConfigPlans
|
||||||
entstehen nur über den Job **`Generate Config Plans`** (`plan_type=manual` + `commands`-TextVar).
|
entstehen nur über den Job **`Generate Config Plans`** (`plan_type=manual` + `commands`-TextVar).
|
||||||
- **Docker-Management:** Portainer API `https://10.40.20.126:9443`, Key in `mcp/portainer/.env`.
|
- **Docker-Management:** Portainer API `https://10.40.20.126:9443`, Key in `mcp/portainer/.env`.
|
||||||
|
- 🔴 **`pct push` meldet Exit 0, auch wenn es die Quelldatei NICHT lesen kann** (gemessen 2026-09-21).
|
||||||
|
`pct push 130 <fehlende-datei> /etc/caddy/Caddyfile` → `failed to open … for reading`, **`rc=0`**.
|
||||||
|
Ein `&&`/`||` hinter dem Push **beweist nichts**, und ein anschließendes `validate`/`reload` prüft
|
||||||
|
dann die **alte** Datei → **grüne Abnahme bei unverändertem Zustand**. Genau so sah der
|
||||||
|
Nautobot-Umzug am 2026-09-21 „gelandet" aus, während live noch die alte Backend-IP stand
|
||||||
|
(Symptom: 502 bei gesundem Backend). **Regel: Push-Werkzeug wird geprüft, nicht geglaubt** — nach
|
||||||
|
jedem `pct push` den Inhalt vergleichen (`md5sum` diesseits/jenseits in *einem* Remote-Aufruf,
|
||||||
|
Abbruch bei Abweichung). Muster: `push_verified()` in `caddy/deploy.sh`. Dieselbe Klasse wie
|
||||||
|
`nc -z` (09-01) und das `grep|sed`-Maskierungsmuster (08-31): **erst das eigene Werkzeug
|
||||||
|
verdächtigen, dann das Netz.**
|
||||||
|
- 🔴 **Die Core-Adressliste `nautobot` wird von Golden Config NIRGENDS gerendert** (Fund 2026-09-21).
|
||||||
|
Der Mgmt-Zugang des Cores für die Deploy-Pipeline läuft über `src-address-list=nautobot`
|
||||||
|
(Regel `*28`) — die Liste selbst stammt aber aus dem **handgelaufenen**
|
||||||
|
`routeros/tp5-etappe2-whitelists.rsc` und steht in **keinem** Template (`grep address-list` im
|
||||||
|
Template findet nur Kommentare). 🔴 **Ein GC-Recovery von null erzeugt damit einen Core, in den
|
||||||
|
Nautobot nicht mehr per SSH kommt** — die Pipeline wäre tot und könnte sich nicht selbst
|
||||||
|
reparieren. Gleiche Lücke wie die DHCP-Reservierungen. **Merkregel: was auf dem Gerät existiert,
|
||||||
|
aber nicht im Template, ist eine Recovery-Lücke, auch wenn Compliance 140/140 grün meldet** —
|
||||||
|
die Zahl prüft nur, was gerendert wird.
|
||||||
Ablösung durch Komodo (native MCP) geplant.
|
Ablösung durch Komodo (native MCP) geplant.
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -248,7 +307,7 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`,
|
|||||||
danach die Ports gegen vorher vergleichen**, sonst verliert man still einen Dienst.
|
danach die Ports gegen vorher vergleichen**, sonst verliert man still einen Dienst.
|
||||||
- **VLAN20-Docker-Hosts: Host-DNS nach dem Umzug prüfen (2026-07-10, TP14.2a):** NAS + pve-LXC zeigten `/etc/resolv.conf` noch auf totes `.41.254` → `docker pull` ghcr scheitert (`EAI_AGAIN`, aber Egress ok!). Fix = `.20.1`: LXC `pct set 400 --nameserver 10.40.20.1`; **Synology-Falle: DSM-„Allgemein"-DNS ≠ was `dockerd` liest** — Interface zieht DNS per DHCP → Fix an der *Netzwerkschnittstelle* „DNS manuell". Laufende Container frieren den `127.0.0.11`-Upstream zum Erstellzeitpunkt ein (Recreate nötig). Merkregel: `docker-pull-hängt` + `dns.lookup EAI_AGAIN` aber `TCP zu ghcr-IP:443 ok` = toter Resolver, nicht Egress.
|
- **VLAN20-Docker-Hosts: Host-DNS nach dem Umzug prüfen (2026-07-10, TP14.2a):** NAS + pve-LXC zeigten `/etc/resolv.conf` noch auf totes `.41.254` → `docker pull` ghcr scheitert (`EAI_AGAIN`, aber Egress ok!). Fix = `.20.1`: LXC `pct set 400 --nameserver 10.40.20.1`; **Synology-Falle: DSM-„Allgemein"-DNS ≠ was `dockerd` liest** — Interface zieht DNS per DHCP → Fix an der *Netzwerkschnittstelle* „DNS manuell". Laufende Container frieren den `127.0.0.11`-Upstream zum Erstellzeitpunkt ein (Recreate nötig). Merkregel: `docker-pull-hängt` + `dns.lookup EAI_AGAIN` aber `TCP zu ghcr-IP:443 ok` = toter Resolver, nicht Egress.
|
||||||
- 📜 **~~ALLNET VLAN-DB (TP1)~~ — historisch.** Die VLAN-Semantik gilt weiter (**40=MEDIA, 50=UNTRUSTED**), die Portangaben (GE1/GE2/GE8) beziehen sich auf ein ausgebautes Gerät.
|
- 📜 **~~ALLNET VLAN-DB (TP1)~~ — historisch.** Die VLAN-Semantik gilt weiter (**40=MEDIA, 50=UNTRUSTED**), die Portangaben (GE1/GE2/GE8) beziehen sich auf ein ausgebautes Gerät.
|
||||||
- **ap2 hängt an `crs326-branch ether24`** (Trunk, native VLAN1 + tagged 10-99); **branch-Uplink `ether1` → `crs326-core ether8`** (korrigiert 2026-08-16 — die frühere Angabe „→ sw1 GE8" stammt aus der Zeit vor dem Core-Tausch; MAC-hart belegt in TP22 Schritt 5).
|
- **ap3 hängt an `crs326-branch ether24`** (Trunk, native VLAN1 + tagged 10-99); **branch-Uplink `ether1` → `crs326-core ether8`** (korrigiert 2026-08-16 — die frühere Angabe „→ sw1 GE8" stammt aus der Zeit vor dem Core-Tausch; MAC-hart belegt in TP22 Schritt 5). ap2 hängt seit dem TP25-Tausch (2026-09-15) an `core ether21` (Technik UG), ap1 an `core ether3` (Sideboard).
|
||||||
- **Mesh = NEIN** (beide APs haben wired Trunk-Uplink → Loop-Risiko).
|
- **Mesh = NEIN** (beide APs haben wired Trunk-Uplink → Loop-Risiko).
|
||||||
- **Beide APs = CAPs, Manager ist `crs326-core` (TP28 ✅ 2026-08-17).** Der Entscheid „kein CAPsMAN" von 2026-07-22 ist **revidiert**: er galt der Konstellation ap1=Manager+AP, in der ein Manager seine eigenen Radios nicht provisionieren kann — mit dem Core als Manager sind beide APs symmetrisch. Der Core braucht dafür **kein** `wifi-qcom`, `capsman` steckt im Basispaket.
|
- **Beide APs = CAPs, Manager ist `crs326-core` (TP28 ✅ 2026-08-17).** Der Entscheid „kein CAPsMAN" von 2026-07-22 ist **revidiert**: er galt der Konstellation ap1=Manager+AP, in der ein Manager seine eigenen Radios nicht provisionieren kann — mit dem Core als Manager sind beide APs symmetrisch. Der Core braucht dafür **kein** `wifi-qcom`, `capsman` steckt im Basispaket.
|
||||||
- 🔴 **`/interface wifi scan` geht am CAP NICHT — nur am Manager** (Fund 2026-08-23). Das Gerät
|
- 🔴 **`/interface wifi scan` geht am CAP NICHT — nur am Manager** (Fund 2026-08-23). Das Gerät
|
||||||
|
|||||||
18
__init__.py
Normal file
18
__init__.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"""Macht die bf-infra-Repo-Wurzel zu einem regulären Python-Paket.
|
||||||
|
|
||||||
|
🔴 LOAD-BEARING — NICHT löschen (sonst laden die Nautobot-Jobs nicht mehr).
|
||||||
|
|
||||||
|
Nautobot klont dieses Repo nach GIT_ROOT/bf_infra/ und lädt Jobs über
|
||||||
|
`import_modules_privately()` → `pkgutil.walk_packages([GIT_ROOT])`. walk_packages
|
||||||
|
entdeckt ausschließlich *reguläre* Pakete (mit __init__.py), niemals
|
||||||
|
Namespace-Packages (Verzeichnisse ohne __init__.py). Ohne diese Datei ist
|
||||||
|
`bf_infra` für den Loader unsichtbar und `jobs/` wird nie importiert — Symptom:
|
||||||
|
Git-Sync SUCCESS, aber Log "No jobs were registered on loading the
|
||||||
|
`bf_infra.jobs` submodule" und kein Job-DB-Eintrag.
|
||||||
|
|
||||||
|
Der module_path-Filter des Loaders (["bf_infra","jobs"]) importiert nur
|
||||||
|
`bf_infra` + `bf_infra.jobs.*`; die übrigen Verzeichnisse (nautobot/, routeros/,
|
||||||
|
bin/, …) bleiben unangetastet und laufen weiter als Standalone-Skripte.
|
||||||
|
|
||||||
|
Verifiziert 2026-09-10 (Nautobot 3.1.7, Python 3.14) — TP44 Teil B.
|
||||||
|
"""
|
||||||
@@ -13,7 +13,9 @@ http:
|
|||||||
session_ttl: 30d
|
session_ttl: 30d
|
||||||
users:
|
users:
|
||||||
- name: admin
|
- name: admin
|
||||||
password: $2a$10$ue8PEedG4t2JRyT83XiGHeqIU4PFRzhn8rjDOZGYGt0sFC2qju2fi
|
password: '!on-device (siehe ~/.secrets + Gerät)'
|
||||||
|
- name: automation
|
||||||
|
password: '!on-device (siehe ~/.secrets + Gerät)'
|
||||||
auth_attempts: 5
|
auth_attempts: 5
|
||||||
block_auth_min: 15
|
block_auth_min: 15
|
||||||
http_proxy: ""
|
http_proxy: ""
|
||||||
@@ -69,7 +71,7 @@ dns:
|
|||||||
ipset_file: ""
|
ipset_file: ""
|
||||||
bootstrap_prefer_ipv6: false
|
bootstrap_prefer_ipv6: false
|
||||||
upstream_timeout: 10s
|
upstream_timeout: 10s
|
||||||
private_networks: []
|
private_<redacted>: []
|
||||||
use_private_ptr_resolvers: true
|
use_private_ptr_resolvers: true
|
||||||
local_ptr_upstreams: []
|
local_ptr_upstreams: []
|
||||||
use_dns64: false
|
use_dns64: false
|
||||||
@@ -202,69 +204,9 @@ filtering:
|
|||||||
- domain: gitea.emsenhuber.net
|
- domain: gitea.emsenhuber.net
|
||||||
answer: 10.40.20.40
|
answer: 10.40.20.40
|
||||||
enabled: true
|
enabled: true
|
||||||
- domain: nautobot.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: ds-bf.emsenhuber.net
|
- domain: ds-bf.emsenhuber.net
|
||||||
answer: 10.40.20.40
|
answer: 10.40.20.40
|
||||||
enabled: true
|
enabled: true
|
||||||
- domain: ccu.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: iobroker.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: z2m25.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: pve-mcp.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: ha-mcp.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: n8n-mcp.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: gitea-mcp.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: pve.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: portainer.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: pbs.emsenhuber.net
|
|
||||||
answer: 10.40.20.40
|
|
||||||
enabled: true
|
|
||||||
- domain: macmini.emsenhuber.net
|
|
||||||
answer: 10.40.20.167
|
|
||||||
enabled: true
|
|
||||||
- domain: mfc.emsenhuber.net
|
|
||||||
answer: 10.40.10.6
|
|
||||||
enabled: true
|
|
||||||
- domain: tv.emsenhuber.net
|
|
||||||
answer: 10.40.40.11
|
|
||||||
enabled: true
|
|
||||||
- domain: docker.emsenhuber.net
|
|
||||||
answer: 10.40.20.126
|
|
||||||
enabled: true
|
|
||||||
- domain: platform-pg.emsenhuber.net
|
|
||||||
answer: 10.40.20.20
|
|
||||||
enabled: true
|
|
||||||
- domain: core.emsenhuber.net
|
|
||||||
answer: 10.40.99.1
|
|
||||||
enabled: true
|
|
||||||
- domain: ap1.emsenhuber.net
|
|
||||||
answer: 10.40.99.11
|
|
||||||
enabled: true
|
|
||||||
- domain: ap2.emsenhuber.net
|
|
||||||
answer: 10.40.99.12
|
|
||||||
enabled: true
|
|
||||||
- domain: branch.emsenhuber.net
|
|
||||||
answer: 10.40.99.2
|
|
||||||
enabled: true
|
|
||||||
- domain: nas.bf.emsenhuber.net
|
- domain: nas.bf.emsenhuber.net
|
||||||
answer: 10.40.20.10
|
answer: 10.40.20.10
|
||||||
enabled: true
|
enabled: true
|
||||||
@@ -316,7 +258,232 @@ clients:
|
|||||||
rdns: true
|
rdns: true
|
||||||
dhcp: true
|
dhcp: true
|
||||||
hosts: true
|
hosts: true
|
||||||
persistent: []
|
persistent:
|
||||||
|
- safe_search:
|
||||||
|
enabled: false
|
||||||
|
bing: false
|
||||||
|
duckduckgo: false
|
||||||
|
ecosia: false
|
||||||
|
google: false
|
||||||
|
pixabay: false
|
||||||
|
yandex: false
|
||||||
|
youtube: false
|
||||||
|
blocked_services:
|
||||||
|
schedule:
|
||||||
|
time_zone: Local
|
||||||
|
ids: []
|
||||||
|
name: dns-bypass-exempt
|
||||||
|
ids:
|
||||||
|
- 10.40.10.82
|
||||||
|
- 10.40.10.183
|
||||||
|
tags: []
|
||||||
|
upstreams: []
|
||||||
|
uid: 381d431a-1a43-41ea-adaa-2d67ada9aab0
|
||||||
|
upstreams_cache_size: 0
|
||||||
|
upstreams_cache_enabled: false
|
||||||
|
use_global_settings: false
|
||||||
|
filtering_enabled: false
|
||||||
|
parental_enabled: false
|
||||||
|
safebrowsing_enabled: false
|
||||||
|
use_global_blocked_services: true
|
||||||
|
ignore_querylog: false
|
||||||
|
ignore_statistics: false
|
||||||
|
- safe_search:
|
||||||
|
enabled: true
|
||||||
|
bing: false
|
||||||
|
duckduckgo: false
|
||||||
|
ecosia: false
|
||||||
|
google: false
|
||||||
|
pixabay: false
|
||||||
|
yandex: false
|
||||||
|
youtube: false
|
||||||
|
blocked_services:
|
||||||
|
schedule:
|
||||||
|
time_zone: Local
|
||||||
|
ids: []
|
||||||
|
name: grp-guest
|
||||||
|
ids:
|
||||||
|
- 10.40.90.0/24
|
||||||
|
tags: []
|
||||||
|
upstreams: []
|
||||||
|
uid: af70dd86-43a9-4165-8228-26c7b90db5e6
|
||||||
|
upstreams_cache_size: 0
|
||||||
|
upstreams_cache_enabled: false
|
||||||
|
use_global_settings: false
|
||||||
|
filtering_enabled: true
|
||||||
|
parental_enabled: true
|
||||||
|
safebrowsing_enabled: false
|
||||||
|
use_global_blocked_services: true
|
||||||
|
ignore_querylog: false
|
||||||
|
ignore_statistics: false
|
||||||
|
- safe_search:
|
||||||
|
enabled: false
|
||||||
|
bing: false
|
||||||
|
duckduckgo: false
|
||||||
|
ecosia: false
|
||||||
|
google: false
|
||||||
|
pixabay: false
|
||||||
|
yandex: false
|
||||||
|
youtube: false
|
||||||
|
blocked_services:
|
||||||
|
schedule:
|
||||||
|
time_zone: Local
|
||||||
|
ids: []
|
||||||
|
name: grp-infra
|
||||||
|
ids:
|
||||||
|
- 10.40.20.0/24
|
||||||
|
tags: []
|
||||||
|
upstreams: []
|
||||||
|
uid: 78b2da82-1946-41ee-b386-b04eaee49bc3
|
||||||
|
upstreams_cache_size: 0
|
||||||
|
upstreams_cache_enabled: false
|
||||||
|
use_global_settings: false
|
||||||
|
filtering_enabled: false
|
||||||
|
parental_enabled: false
|
||||||
|
safebrowsing_enabled: false
|
||||||
|
use_global_blocked_services: true
|
||||||
|
ignore_querylog: false
|
||||||
|
ignore_statistics: false
|
||||||
|
- safe_search:
|
||||||
|
enabled: false
|
||||||
|
bing: false
|
||||||
|
duckduckgo: false
|
||||||
|
ecosia: false
|
||||||
|
google: false
|
||||||
|
pixabay: false
|
||||||
|
yandex: false
|
||||||
|
youtube: false
|
||||||
|
blocked_services:
|
||||||
|
schedule:
|
||||||
|
time_zone: Local
|
||||||
|
ids: []
|
||||||
|
name: grp-iot
|
||||||
|
ids:
|
||||||
|
- 10.40.30.0/24
|
||||||
|
tags: []
|
||||||
|
upstreams: []
|
||||||
|
uid: 7a8ac674-4365-4b06-ad51-2516400903a1
|
||||||
|
upstreams_cache_size: 0
|
||||||
|
upstreams_cache_enabled: false
|
||||||
|
use_global_settings: true
|
||||||
|
filtering_enabled: true
|
||||||
|
parental_enabled: false
|
||||||
|
safebrowsing_enabled: false
|
||||||
|
use_global_blocked_services: true
|
||||||
|
ignore_querylog: false
|
||||||
|
ignore_statistics: false
|
||||||
|
- safe_search:
|
||||||
|
enabled: false
|
||||||
|
bing: false
|
||||||
|
duckduckgo: false
|
||||||
|
ecosia: false
|
||||||
|
google: false
|
||||||
|
pixabay: false
|
||||||
|
yandex: false
|
||||||
|
youtube: false
|
||||||
|
blocked_services:
|
||||||
|
schedule:
|
||||||
|
time_zone: Local
|
||||||
|
ids: []
|
||||||
|
name: grp-main
|
||||||
|
ids:
|
||||||
|
- 10.40.10.0/24
|
||||||
|
tags: []
|
||||||
|
upstreams: []
|
||||||
|
uid: 39955cc1-6ec2-4cf6-a757-233672b9dc3d
|
||||||
|
upstreams_cache_size: 0
|
||||||
|
upstreams_cache_enabled: false
|
||||||
|
use_global_settings: true
|
||||||
|
filtering_enabled: true
|
||||||
|
parental_enabled: false
|
||||||
|
safebrowsing_enabled: false
|
||||||
|
use_global_blocked_services: true
|
||||||
|
ignore_querylog: false
|
||||||
|
ignore_statistics: false
|
||||||
|
- safe_search:
|
||||||
|
enabled: false
|
||||||
|
bing: false
|
||||||
|
duckduckgo: false
|
||||||
|
ecosia: false
|
||||||
|
google: false
|
||||||
|
pixabay: false
|
||||||
|
yandex: false
|
||||||
|
youtube: false
|
||||||
|
blocked_services:
|
||||||
|
schedule:
|
||||||
|
time_zone: Local
|
||||||
|
ids: []
|
||||||
|
name: grp-media
|
||||||
|
ids:
|
||||||
|
- 10.40.40.0/24
|
||||||
|
tags: []
|
||||||
|
upstreams: []
|
||||||
|
uid: 44e4a1d7-d1b6-446c-a753-65e714ec93aa
|
||||||
|
upstreams_cache_size: 0
|
||||||
|
upstreams_cache_enabled: false
|
||||||
|
use_global_settings: false
|
||||||
|
filtering_enabled: false
|
||||||
|
parental_enabled: false
|
||||||
|
safebrowsing_enabled: false
|
||||||
|
use_global_blocked_services: true
|
||||||
|
ignore_querylog: false
|
||||||
|
ignore_statistics: false
|
||||||
|
- safe_search:
|
||||||
|
enabled: false
|
||||||
|
bing: false
|
||||||
|
duckduckgo: false
|
||||||
|
ecosia: false
|
||||||
|
google: false
|
||||||
|
pixabay: false
|
||||||
|
yandex: false
|
||||||
|
youtube: false
|
||||||
|
blocked_services:
|
||||||
|
schedule:
|
||||||
|
time_zone: Local
|
||||||
|
ids: []
|
||||||
|
name: grp-mgmt
|
||||||
|
ids:
|
||||||
|
- 10.40.99.0/24
|
||||||
|
tags: []
|
||||||
|
upstreams: []
|
||||||
|
uid: 4c5eeafb-f7f2-4fb1-ab87-fd2a2366d57a
|
||||||
|
upstreams_cache_size: 0
|
||||||
|
upstreams_cache_enabled: false
|
||||||
|
use_global_settings: false
|
||||||
|
filtering_enabled: false
|
||||||
|
parental_enabled: false
|
||||||
|
safebrowsing_enabled: false
|
||||||
|
use_global_blocked_services: true
|
||||||
|
ignore_querylog: false
|
||||||
|
ignore_statistics: false
|
||||||
|
- safe_search:
|
||||||
|
enabled: false
|
||||||
|
bing: false
|
||||||
|
duckduckgo: false
|
||||||
|
ecosia: false
|
||||||
|
google: false
|
||||||
|
pixabay: false
|
||||||
|
yandex: false
|
||||||
|
youtube: false
|
||||||
|
blocked_services:
|
||||||
|
schedule:
|
||||||
|
time_zone: Local
|
||||||
|
ids: []
|
||||||
|
name: grp-untrusted
|
||||||
|
ids:
|
||||||
|
- 10.40.50.0/24
|
||||||
|
tags: []
|
||||||
|
upstreams: []
|
||||||
|
uid: 7f182498-7b82-4580-b1b0-0a2d46c205ef
|
||||||
|
upstreams_cache_size: 0
|
||||||
|
upstreams_cache_enabled: false
|
||||||
|
use_global_settings: true
|
||||||
|
filtering_enabled: true
|
||||||
|
parental_enabled: false
|
||||||
|
safebrowsing_enabled: false
|
||||||
|
use_global_blocked_services: true
|
||||||
|
ignore_querylog: false
|
||||||
|
ignore_statistics: false
|
||||||
log:
|
log:
|
||||||
enabled: true
|
enabled: true
|
||||||
file: ""
|
file: ""
|
||||||
|
|||||||
75
adguard/README.md
Normal file
75
adguard/README.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# AdGuard Home — `agh-bf1` (.36) / `agh-bf2` (.37) · TP23 Stufe 2/4/5
|
||||||
|
|
||||||
|
Das AGH-**Paar** (pve LXC 110/111, `10.40.20.36` / `.37`) ist der Client-Resolver des BF-Netzes
|
||||||
|
(DHCP liefert beide in allen 7 VLANs). Upstream: DoH zum CF-Gateway; conditional
|
||||||
|
`[/bf.emsenhuber.net/] → 10.40.20.1`. **Kein natives HA** → Sync ist Betriebsaufwand.
|
||||||
|
|
||||||
|
## Dateien
|
||||||
|
| Datei | Zweck |
|
||||||
|
|---|---|
|
||||||
|
| `AdGuardHome.yaml` | **Repo-SSoT** der Config. **Passwort-Hashes REDIGIERT** (`'<redacted>'`) — keine Secrets im Git. Echte Hashes leben auf den Geräten; Klartext des API-Users in `~/.secrets`. |
|
||||||
|
| `agh-sync.py` | `check` (Drift `.36`↔`.37`) · `push` (SSoT auf beide, gestaffelter Restart, reinjiziert Hashes live). |
|
||||||
|
| `agh-exception.py` | Exception-Primitive (Stufe 5) gegen **beide** Instanzen. |
|
||||||
|
|
||||||
|
## Client-Policy (Stufe 4, `clients.persistent`)
|
||||||
|
Ein VLAN = eine CIDR-Gruppe. Alle nutzen denselben CF-Upstream-Filter; Divergenz = per-Client-Toggle.
|
||||||
|
|
||||||
|
| Gruppe | CIDR | filtering | safe_search | parental |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| grp-main | 10.40.10.0/24 | an (global) | – | – |
|
||||||
|
| grp-infra | 10.40.20.0/24 | **aus** | – | – |
|
||||||
|
| grp-iot | 10.40.30.0/24 | an | – | – |
|
||||||
|
| grp-media | 10.40.40.0/24 | **aus** | – | – |
|
||||||
|
| grp-untrusted | 10.40.50.0/24 | an | – | – |
|
||||||
|
| grp-guest | 10.40.90.0/24 | an | **on** | **on** |
|
||||||
|
| grp-mgmt | 10.40.99.0/24 | **aus** | – | – |
|
||||||
|
| dns-bypass-exempt | 10.40.10.183 + .82 | **aus** | – | – |
|
||||||
|
|
||||||
|
`filtering aus` bei INFRA/MEDIA/MGMT = bewusste Reduktion (Streaming/Server/Mgmt nicht brechen), per Gruppe rückholbar.
|
||||||
|
|
||||||
|
## Personen-Clients (Stufe 5 Redesign 2026-09-12) — SSoT für Person→Gerät
|
||||||
|
Zusätzlich zu den VLAN-CIDR-Gruppen bekommt **jede Person einen eigenen AGH-Client**
|
||||||
|
(`/control/clients`): `name` = Personenname, `ids` = **alle** Geräte-IPs der Person. Das ist der
|
||||||
|
**einzige** Ort, an dem Person→Gerät gepflegt wird (kein zweites Mapping-File). Damit:
|
||||||
|
- wirken Self-Service-Ausnahmen **pro Person**: `@@||domain^$client='Personenname'` statt global;
|
||||||
|
- filtert `recent-blocked --client <person>` das Query-Log auf **ihre** Geräte;
|
||||||
|
- bleiben Ausnahmen stabil bei Gerätewechsel (Name statt IP).
|
||||||
|
|
||||||
|
🔴 **On-network verifizieren, bevor Stufe 6 scharf geht:** dass AGH `$client=` auf **Allow**-Regeln
|
||||||
|
(`@@`) in der laufenden Version greift. Fällt der Test negativ aus → Fallback `$client=<ip>|<ip>`
|
||||||
|
(die Geräte-IPs); `agh-exception.py scoped_rule()` kann beides. Die CIDR-Gruppen (Policy-Grenze)
|
||||||
|
bleiben unberührt — Personen-Clients tragen **nur** die Ausnahmen.
|
||||||
|
|
||||||
|
## Sync-Invariante
|
||||||
|
AGH **schreibt die YAML beim Restart um** (normalisiert) → **nicht** gegen die Repo-Datei diffen,
|
||||||
|
sondern **`.36` == `.37`**. Genau das prüft `agh-sync.py check`.
|
||||||
|
|
||||||
|
## Exception-Contract (Stufe 5 — was die HA-Schicht auslöst)
|
||||||
|
Auth: Basic, User `automation` (`~/.secrets` → `AGH_AUTOMATION_*`). **Immer auf `.36` UND `.37`.**
|
||||||
|
|
||||||
|
🔴 **Wichtig für D:** die beiden AGH-Endpunkte sind **read-modify-write**, kein simples Append —
|
||||||
|
`set_rules` **ersetzt die ganze Liste**, `clients/update` braucht das **volle Client-Objekt**.
|
||||||
|
Ein zustandsloses HA-`rest_command` reicht deshalb **nicht**; `agh-exception.py` ist die
|
||||||
|
Referenz-RMW-Implementierung, die D aufruft (Deploy-Weg auf HAOS in Etappe D zu entscheiden:
|
||||||
|
`shell_command`/`command_line` gegen die deployte CLI **oder** ein dünner Wrapper-Endpunkt).
|
||||||
|
|
||||||
|
| Aktion | CLI | AGH-API (RMW) |
|
||||||
|
|---|---|---|
|
||||||
|
| Domain **pro Person** entsperren | `allow-domain <d> --client <person>` | `GET /control/filtering/status` → +`@@\|\|<d>^$client='<person>'` → `POST /control/filtering/set_rules` |
|
||||||
|
| Domain global entsperren (Admin) | `allow-domain <d>` | dito, ohne `$client` |
|
||||||
|
| Entsperrung zurück | `unallow-domain <d> [--client <person>]` | dito, exakt dieselbe Regel entfernen |
|
||||||
|
| „meine Ausnahmen" | `list-exceptions --client <person> --json` | `GET /control/filtering/status`, `@@`-Regeln parsen/filtern |
|
||||||
|
| „zuletzt geblockt" | `recent-blocked --client <person> --json` | `GET /control/querylog?response_status=blocked`, client-seitig auf Geräte-IPs gefiltert |
|
||||||
|
| Client entfiltern (grob) | `unfilter <ip>` | `GET /control/clients` → Objekt, `filtering_enabled=false` → `POST /control/clients/update` |
|
||||||
|
| Client wieder filtern | `refilter <ip>` | dito, zurück auf global |
|
||||||
|
|
||||||
|
Reine Regel-/Filter-Logik ist netzunabhängig getestet: `python3 agh-exception.py selftest`.
|
||||||
|
|
||||||
|
**Kein Timing in der CLI** — Ablauf/Revert macht der Aufrufer (HA-Timer) und, ab Stufe 6, der
|
||||||
|
RouterOS-Backstop.
|
||||||
|
|
||||||
|
## RouterOS-Backstop (Stufe 6, noch NICHT gebaut)
|
||||||
|
Analog `jit-admin` (`routeros/tp5-etappe4-input-mgmt.rsc`): Address-List `jit-dns-bypass` mit
|
||||||
|
`timeout` + Forward-Regel, die dieser Liste 53/853 nach außen erlaubt — der Fail-safe, falls HA
|
||||||
|
den Revert verpasst. Gebaut via **Nautobot GC** (kein Direkt-Write), erst wenn die Bypass-Sperre
|
||||||
|
scharf geschaltet wird.
|
||||||
287
adguard/agh-exception.py
Executable file
287
adguard/agh-exception.py
Executable file
@@ -0,0 +1,287 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
agh-exception.py — TP23 Stufe 5 Exception-Primitive gegen das AGH-Paar (.36 + .37).
|
||||||
|
|
||||||
|
Von der HA-Self-Service-Schicht (shell_command) ODER von Hand aufrufbar. Wirkt IMMER auf
|
||||||
|
BEIDE Instanzen (kein Sync im Datenpfad). KEIN eigenes Timing — Ablauf/Revert macht der
|
||||||
|
Aufrufer (HA-Timer) bzw. der RouterOS-Backstop (Stufe 6). Diese CLI ist reines add/remove/read.
|
||||||
|
|
||||||
|
allow-domain <domain> [--client <name|ip[,ip]>] → user_rules += @@||domain^[$client=…]
|
||||||
|
unallow-domain <domain> [--client <name|ip[,ip]>] → dieselbe Regel wieder entfernen
|
||||||
|
list-exceptions [--client <name>] [--json] → alle @@-Regeln, nach Client gruppiert
|
||||||
|
recent-blocked --client <name|ip[,ip]> [--limit N] [--json]
|
||||||
|
→ zuletzt geblockte Domains DIESER Person
|
||||||
|
unfilter <ip> → Client (dessen ids <ip> enthält) filtering_enabled=false (grob!)
|
||||||
|
refilter <ip> → denselben Client wieder filtern (use_global_settings=true)
|
||||||
|
status → user_rules + Client-Policy beider Instanzen
|
||||||
|
|
||||||
|
--client:
|
||||||
|
* ein AGH-Client-NAME (empfohlen, stabil bei Gerätewechsel) → Regel bekommt $client='Name',
|
||||||
|
IPs werden für recent-blocked aus /control/clients (ids) aufgelöst.
|
||||||
|
* ODER eine/mehrere IP/CIDR, komma-getrennt → $client=<ip>|<ip>, recent-blocked filtert darauf.
|
||||||
|
ohne --client bei allow/unallow = GLOBAL (für alle) — nur für Admin-Zwecke.
|
||||||
|
|
||||||
|
🔴 ANNAHME, on-network zu verifizieren, BEVOR Stufe 6 scharf geht: dass AGH $client= auf
|
||||||
|
ALLOW-Regeln (@@) in der laufenden Version wirkt. Die Regelkonstruktion steht isoliert in
|
||||||
|
scoped_rule()/parse_allow_rule() — falls die Syntax abweicht, nur dort anpassen.
|
||||||
|
|
||||||
|
Auth: AGH_AUTOMATION_USER/_PASS aus ~/.secrets/credentials.env (nie in argv).
|
||||||
|
"""
|
||||||
|
import sys, os, json, base64, urllib.request, urllib.error, urllib.parse, pathlib, re, ipaddress, argparse
|
||||||
|
|
||||||
|
DOMAIN_RE = re.compile(r"^(?=.{1,253}$)([a-z0-9](-?[a-z0-9]+)*\.)+[a-z]{2,}$", re.I)
|
||||||
|
IP_RE = re.compile(r"^\d{1,3}(\.\d{1,3}){3}$")
|
||||||
|
CIDR_RE = re.compile(r"^\d{1,3}(\.\d{1,3}){3}/\d{1,2}$")
|
||||||
|
# Ein Client-„spec" ist IP-artig, wenn JEDES komma-Glied IP oder CIDR ist; sonst = Name.
|
||||||
|
|
||||||
|
INST = ["10.40.20.36", "10.40.20.37"]
|
||||||
|
# Creds-Quellen in Reihenfolge: erste, die User+Pass liefert, gewinnt.
|
||||||
|
# So läuft derselbe Code auf dem Mac (~/.secrets) und auf HAOS (/config).
|
||||||
|
CRED_FILES = [
|
||||||
|
os.environ.get("AGH_CREDS_FILE"),
|
||||||
|
str(pathlib.Path.home() / ".secrets/credentials.env"),
|
||||||
|
"/config/agh-exception.secrets",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def _from_file(path):
|
||||||
|
u = p = None
|
||||||
|
try:
|
||||||
|
for line in open(path).read().splitlines():
|
||||||
|
if line.startswith("AGH_AUTOMATION_USER="): u = line.split("=", 1)[1].strip()
|
||||||
|
if line.startswith("AGH_AUTOMATION_PASS="): p = line.split("=", 1)[1].strip()
|
||||||
|
except OSError:
|
||||||
|
return None
|
||||||
|
return (u, p) if (u and p) else None
|
||||||
|
|
||||||
|
|
||||||
|
def creds():
|
||||||
|
eu, ep = os.environ.get("AGH_AUTOMATION_USER"), os.environ.get("AGH_AUTOMATION_PASS")
|
||||||
|
if eu and ep:
|
||||||
|
return eu, ep
|
||||||
|
for f in CRED_FILES:
|
||||||
|
if f and (r := _from_file(f)):
|
||||||
|
return r
|
||||||
|
sys.exit("AGH_AUTOMATION_USER/_PASS: weder Env noch eine Creds-Datei gefunden")
|
||||||
|
|
||||||
|
|
||||||
|
_AUTH = None
|
||||||
|
def auth_header():
|
||||||
|
# lazy: erst beim ersten echten API-Call — so bleibt das Modul ohne Creds importier-/testbar.
|
||||||
|
global _AUTH
|
||||||
|
if _AUTH is None:
|
||||||
|
_AUTH = "Basic " + base64.b64encode(":".join(creds()).encode()).decode()
|
||||||
|
return _AUTH
|
||||||
|
|
||||||
|
|
||||||
|
def api(ip, path, method="GET", body=None):
|
||||||
|
url = f"http://{ip}{path}"
|
||||||
|
data = json.dumps(body).encode() if body is not None else None
|
||||||
|
req = urllib.request.Request(url, data=data, method=method,
|
||||||
|
headers={"Authorization": auth_header(),
|
||||||
|
"Content-Type": "application/json"})
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=8) as r:
|
||||||
|
raw = r.read()
|
||||||
|
return json.loads(raw) if raw else {}
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
sys.exit(f"{ip} {path} → HTTP {e.code}: {e.read().decode()[:200]}")
|
||||||
|
except Exception as e:
|
||||||
|
sys.exit(f"{ip} {path} → {e}")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Reine Regel-Logik (netzunabhängig, self-testbar). Bei AGH-Syntax-Drift NUR hier anfassen.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def is_ip_spec(spec):
|
||||||
|
"""True, wenn spec eine (Komma-)Liste aus lauter IPs/CIDRs ist (→ IP-spec), sonst Name."""
|
||||||
|
parts = [s.strip() for s in spec.split(",") if s.strip()]
|
||||||
|
return bool(parts) and all(IP_RE.match(p) or CIDR_RE.match(p) for p in parts)
|
||||||
|
|
||||||
|
|
||||||
|
def client_modifier(spec):
|
||||||
|
"""$client-Modifier-Wert für eine Regel. IP-spec → ip|ip ; Name → 'Name' (quoted)."""
|
||||||
|
if is_ip_spec(spec):
|
||||||
|
return "|".join(s.strip() for s in spec.split(",") if s.strip())
|
||||||
|
return "'" + spec.replace("'", "") + "'"
|
||||||
|
|
||||||
|
|
||||||
|
def scoped_rule(domain, client=None):
|
||||||
|
"""@@||domain^ bzw. @@||domain^$client=… (client = Name oder ip[,ip])."""
|
||||||
|
base = f"@@||{domain}^"
|
||||||
|
return base if not client else f"{base}$client={client_modifier(client)}"
|
||||||
|
|
||||||
|
|
||||||
|
_ALLOW_RE = re.compile(r"^@@\|\|(?P<domain>[^\^]+)\^(?:\$client=(?P<client>.+))?$")
|
||||||
|
def parse_allow_rule(rule):
|
||||||
|
"""@@-Regel → {'domain':…, 'client': <str oder None>, 'raw': rule} oder None."""
|
||||||
|
m = _ALLOW_RE.match(rule.strip())
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
return {"domain": m.group("domain"), "client": m.group("client"), "raw": rule.strip()}
|
||||||
|
|
||||||
|
|
||||||
|
def ip_in_specs(ip, specs):
|
||||||
|
"""Ist ip (aus dem Query-Log) von einer der specs (IP oder CIDR) erfasst?"""
|
||||||
|
try:
|
||||||
|
addr = ipaddress.ip_address(ip)
|
||||||
|
except ValueError:
|
||||||
|
return False
|
||||||
|
for s in specs:
|
||||||
|
s = s.strip()
|
||||||
|
try:
|
||||||
|
if "/" in s:
|
||||||
|
if addr in ipaddress.ip_network(s, strict=False):
|
||||||
|
return True
|
||||||
|
elif ip == s:
|
||||||
|
return True
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
def resolve_client_ips(spec):
|
||||||
|
"""spec → Liste von IP/CIDR-strings. IP-spec direkt; Name → ids des AGH-Clients (von .36)."""
|
||||||
|
if is_ip_spec(spec):
|
||||||
|
return [s.strip() for s in spec.split(",") if s.strip()]
|
||||||
|
data = api(INST[0], "/control/clients")
|
||||||
|
for c in data.get("clients", []):
|
||||||
|
if c.get("name") == spec:
|
||||||
|
return list(c.get("ids", []))
|
||||||
|
sys.exit(f"kein AGH-Client mit name={spec!r} (für IP-spec Komma-Liste aus IP/CIDR angeben)")
|
||||||
|
|
||||||
|
|
||||||
|
def allow(domain, client=None, remove=False):
|
||||||
|
domain = domain.strip().lower()
|
||||||
|
if not DOMAIN_RE.match(domain):
|
||||||
|
sys.exit(f"ungültige Domain: {domain!r}")
|
||||||
|
r = scoped_rule(domain, client)
|
||||||
|
for ip in INST:
|
||||||
|
st = api(ip, "/control/filtering/status")
|
||||||
|
rules = st.get("user_rules", [])
|
||||||
|
if remove:
|
||||||
|
rules = [x for x in rules if x.strip() != r]
|
||||||
|
elif r not in rules:
|
||||||
|
rules = rules + [r]
|
||||||
|
api(ip, "/control/filtering/set_rules", "POST", {"rules": rules})
|
||||||
|
print(f"{ip}: {'entfernt' if remove else 'gesetzt'} {r} (user_rules={len(rules)})")
|
||||||
|
|
||||||
|
|
||||||
|
def list_exceptions(client=None, as_json=False):
|
||||||
|
st = api(INST[0], "/control/filtering/status") # .36 == .37 (Invariante, via agh-sync geprüft)
|
||||||
|
items = []
|
||||||
|
for raw in st.get("user_rules", []):
|
||||||
|
p = parse_allow_rule(raw)
|
||||||
|
if not p:
|
||||||
|
continue
|
||||||
|
if client is not None and (p["client"] or "") != client_modifier(client):
|
||||||
|
continue
|
||||||
|
items.append(p)
|
||||||
|
if as_json:
|
||||||
|
print(json.dumps(items, ensure_ascii=False))
|
||||||
|
return
|
||||||
|
if not items:
|
||||||
|
print("— keine Ausnahmen —"); return
|
||||||
|
for p in items:
|
||||||
|
who = p["client"] or "GLOBAL"
|
||||||
|
print(f" {p['domain']:40} client={who}")
|
||||||
|
|
||||||
|
|
||||||
|
def recent_blocked(client, limit=20, as_json=False):
|
||||||
|
specs = resolve_client_ips(client)
|
||||||
|
q = urllib.parse.urlencode({"response_status": "blocked", "limit": max(limit * 8, 100)})
|
||||||
|
data = api(INST[0], f"/control/querylog?{q}")
|
||||||
|
seen = {} # domain -> {count, last}
|
||||||
|
for e in data.get("data", []):
|
||||||
|
c = e.get("client", "")
|
||||||
|
if not ip_in_specs(c, specs):
|
||||||
|
continue
|
||||||
|
dom = (e.get("question", {}) or {}).get("name", "").rstrip(".").lower()
|
||||||
|
if not dom:
|
||||||
|
continue
|
||||||
|
rec = seen.setdefault(dom, {"domain": dom, "count": 0, "last": e.get("time", "")})
|
||||||
|
rec["count"] += 1
|
||||||
|
if e.get("time", "") > rec["last"]:
|
||||||
|
rec["last"] = e.get("time", "")
|
||||||
|
out = sorted(seen.values(), key=lambda r: r["last"], reverse=True)[:limit]
|
||||||
|
if as_json:
|
||||||
|
print(json.dumps(out, ensure_ascii=False)); return
|
||||||
|
if not out:
|
||||||
|
print("— nichts geblockt —"); return
|
||||||
|
for r in out:
|
||||||
|
print(f" {r['domain']:40} x{r['count']:<4} {r['last']}")
|
||||||
|
|
||||||
|
|
||||||
|
def set_filter(ip_addr, enabled):
|
||||||
|
if not IP_RE.match(ip_addr):
|
||||||
|
sys.exit(f"ungültige IP: {ip_addr!r}")
|
||||||
|
for ip in INST:
|
||||||
|
data = api(ip, "/control/clients")
|
||||||
|
c = None
|
||||||
|
for cand in data.get("clients", []):
|
||||||
|
if ip_addr in cand.get("ids", []):
|
||||||
|
c = cand; break
|
||||||
|
if not c:
|
||||||
|
sys.exit(f"{ip}: kein Client mit id exakt {ip_addr} — für Gruppen die CIDR angeben")
|
||||||
|
c = dict(c)
|
||||||
|
c["use_global_settings"] = enabled
|
||||||
|
c["filtering_enabled"] = enabled
|
||||||
|
api(ip, "/control/clients/update", "POST", {"name": c["name"], "data": c})
|
||||||
|
print(f"{ip}: {c['name']} filtering={'an' if enabled else 'AUS'}")
|
||||||
|
|
||||||
|
|
||||||
|
def status():
|
||||||
|
for ip in INST:
|
||||||
|
st = api(ip, "/control/filtering/status")
|
||||||
|
allows = [r for r in st.get("user_rules", []) if r.startswith("@@")]
|
||||||
|
cl = api(ip, "/control/clients").get("clients", [])
|
||||||
|
print(f"== {ip} == allow-rules={allows or '—'}")
|
||||||
|
for c in cl:
|
||||||
|
print(f" {c['name']:16} ids={c['ids']} global={c['use_global_settings']} filt={c['filtering_enabled']}")
|
||||||
|
|
||||||
|
|
||||||
|
def _selftest():
|
||||||
|
"""Netzunabhängiger Test der reinen Regel-Logik."""
|
||||||
|
assert is_ip_spec("10.40.10.82") and is_ip_spec("10.40.10.82,10.40.10.183")
|
||||||
|
assert is_ip_spec("10.40.10.0/24") and not is_ip_spec("christoph")
|
||||||
|
assert scoped_rule("x.com") == "@@||x.com^"
|
||||||
|
assert scoped_rule("x.com", "10.40.10.82,10.40.10.183") == "@@||x.com^$client=10.40.10.82|10.40.10.183"
|
||||||
|
assert scoped_rule("x.com", "christoph") == "@@||x.com^$client='christoph'"
|
||||||
|
p = parse_allow_rule("@@||x.com^$client='christoph'")
|
||||||
|
assert p and p["domain"] == "x.com" and p["client"] == "'christoph'"
|
||||||
|
p2 = parse_allow_rule("@@||y.com^")
|
||||||
|
assert p2 and p2["client"] is None
|
||||||
|
assert parse_allow_rule("||block.com^") is None
|
||||||
|
assert ip_in_specs("10.40.10.82", ["10.40.10.82"]) is True
|
||||||
|
assert ip_in_specs("10.40.10.5", ["10.40.10.0/24"]) is True
|
||||||
|
assert ip_in_specs("10.40.20.5", ["10.40.10.0/24"]) is False
|
||||||
|
# round-trip: eine scoped Regel wird von list-exceptions --client wieder getroffen
|
||||||
|
assert (parse_allow_rule(scoped_rule("z.com", "christoph"))["client"]
|
||||||
|
== client_modifier("christoph"))
|
||||||
|
print("selftest OK")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
ap = argparse.ArgumentParser(add_help=False)
|
||||||
|
ap.add_argument("cmd", nargs="?")
|
||||||
|
ap.add_argument("arg", nargs="?")
|
||||||
|
ap.add_argument("--client")
|
||||||
|
ap.add_argument("--permanent", action="store_true") # semantisch = kein Timer; wirkt in HA, nicht hier
|
||||||
|
ap.add_argument("--limit", type=int, default=20)
|
||||||
|
ap.add_argument("--json", action="store_true")
|
||||||
|
a = ap.parse_args()
|
||||||
|
|
||||||
|
if not a.cmd:
|
||||||
|
sys.exit(__doc__)
|
||||||
|
if a.cmd == "allow-domain": allow(a.arg, a.client)
|
||||||
|
elif a.cmd == "unallow-domain": allow(a.arg, a.client, remove=True)
|
||||||
|
elif a.cmd == "list-exceptions": list_exceptions(a.client, a.json)
|
||||||
|
elif a.cmd == "recent-blocked":
|
||||||
|
if not a.client: sys.exit("recent-blocked braucht --client <name|ip[,ip]>")
|
||||||
|
recent_blocked(a.client, a.limit, a.json)
|
||||||
|
elif a.cmd == "unfilter": set_filter(a.arg, False)
|
||||||
|
elif a.cmd == "refilter": set_filter(a.arg, True)
|
||||||
|
elif a.cmd == "status": status()
|
||||||
|
elif a.cmd == "selftest": _selftest()
|
||||||
|
else: sys.exit(f"unbekannt: {a.cmd}\n{__doc__}")
|
||||||
113
adguard/agh-sync.py
Executable file
113
adguard/agh-sync.py
Executable file
@@ -0,0 +1,113 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
agh-sync.py — AdGuard-Home-Paar agh-bf1 (.36) / agh-bf2 (.37) synchron halten.
|
||||||
|
|
||||||
|
TP23 Stufe 2/4: die beiden AGH-Instanzen (pve LXC 110/111) haben KEIN natives HA →
|
||||||
|
Config-Sync ist Betriebsaufwand. Dieses Werkzeug ist der Sync.
|
||||||
|
|
||||||
|
SSoT = adguard/AdGuardHome.yaml (Passwort-Hashes REDIGIERT — keine Secrets im Git).
|
||||||
|
Wahrheit im Datenpfad = die beiden Live-Instanzen; die Invariante ist .36 == .37
|
||||||
|
(AGH normalisiert die YAML beim Restart, daher NICHT gegen die Repo-Datei vergleichen,
|
||||||
|
sondern die beiden Live-Configs gegeneinander).
|
||||||
|
|
||||||
|
check pull .36 + .37, Passwörter redigiert diffen → meldet Drift (exit 1 bei Drift)
|
||||||
|
push Repo-SSoT nehmen, echte users-Hashes aus .36 (live) reinjizieren, validieren,
|
||||||
|
auf beide pushen, GESTAFFELT neu starten (110 → prüfen → 111), .36==.37 + API prüfen
|
||||||
|
|
||||||
|
Kein Direkt-`sshpass -p` (Leak). SSH-Key zu root@pve vorausgesetzt.
|
||||||
|
"""
|
||||||
|
import subprocess, sys, tempfile, base64, os, re, pathlib, urllib.request
|
||||||
|
|
||||||
|
PVE = "root@10.40.20.100"
|
||||||
|
INST = {"110": "10.40.20.36", "111": "10.40.20.37"} # ct-id -> ip
|
||||||
|
PRIMARY = "110" # Reihenfolge: primary zuerst
|
||||||
|
SVC = "AdGuardHome.service"
|
||||||
|
CFG = "/opt/AdGuardHome/AdGuardHome.yaml"
|
||||||
|
REPO = pathlib.Path(__file__).resolve().parent
|
||||||
|
SSOT = REPO / "AdGuardHome.yaml"
|
||||||
|
PW_RE = re.compile(r"^( password: ).*$")
|
||||||
|
|
||||||
|
|
||||||
|
def sh(*args, inp=None):
|
||||||
|
return subprocess.run(args, input=inp, capture_output=True, text=True)
|
||||||
|
|
||||||
|
|
||||||
|
def pull(ct):
|
||||||
|
r = sh("ssh", "-o", "ConnectTimeout=8", PVE, f"pct exec {ct} -- cat {CFG}")
|
||||||
|
if r.returncode:
|
||||||
|
sys.exit(f"pull {ct} fehlgeschlagen: {r.stderr.strip()}")
|
||||||
|
return r.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def redact(text):
|
||||||
|
return "\n".join(PW_RE.sub(r"\1'<redacted>'", l) for l in text.splitlines())
|
||||||
|
|
||||||
|
|
||||||
|
def live_users_hashes(ct):
|
||||||
|
"""echte password-Zeilen (in Reihenfolge) aus der Live-Config von ct."""
|
||||||
|
out, inusers = [], False
|
||||||
|
for l in pull(ct).splitlines():
|
||||||
|
if l.startswith("users:"): inusers = True; continue
|
||||||
|
if inusers and l and not l.startswith((" ", "\t")): break
|
||||||
|
if inusers:
|
||||||
|
m = PW_RE.match(l)
|
||||||
|
if m: out.append(l)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_check():
|
||||||
|
a, b = redact(pull("110")), redact(pull("111"))
|
||||||
|
if a == b:
|
||||||
|
print("✅ .36 == .37 (synchron)")
|
||||||
|
return 0
|
||||||
|
print("🔴 DRIFT zwischen .36 und .37:")
|
||||||
|
import difflib
|
||||||
|
for line in difflib.unified_diff(a.splitlines(), b.splitlines(),
|
||||||
|
".36", ".37", lineterm=""):
|
||||||
|
print(line)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_push():
|
||||||
|
if not SSOT.exists():
|
||||||
|
sys.exit(f"SSoT fehlt: {SSOT}")
|
||||||
|
ssot = SSOT.read_text()
|
||||||
|
# echte Hashes aus dem primary reinjizieren (SSoT trägt nur Redaktions-Platzhalter)
|
||||||
|
hashes = live_users_hashes(PRIMARY)
|
||||||
|
red = [i for i, l in enumerate(ssot.splitlines()) if PW_RE.match(l)]
|
||||||
|
if len(red) != len(hashes):
|
||||||
|
sys.exit(f"users-Anzahl SSoT({len(red)}) != live({len(hashes)}) — manuell prüfen")
|
||||||
|
lines = ssot.splitlines()
|
||||||
|
for idx, real in zip(red, hashes):
|
||||||
|
lines[idx] = real
|
||||||
|
merged = "\n".join(lines) + "\n"
|
||||||
|
# validieren
|
||||||
|
try:
|
||||||
|
import yaml
|
||||||
|
doc = yaml.safe_load(merged)
|
||||||
|
assert doc["users"] and doc["clients"]["persistent"]
|
||||||
|
except Exception as e:
|
||||||
|
sys.exit(f"merge invalide: {e}")
|
||||||
|
b64 = base64.b64encode(merged.encode()).decode()
|
||||||
|
order = [PRIMARY] + [c for c in INST if c != PRIMARY]
|
||||||
|
for ct in order:
|
||||||
|
print(f"→ push + restart {ct} ({INST[ct]}) …")
|
||||||
|
r = sh("ssh", "-o", "ConnectTimeout=10", PVE,
|
||||||
|
f"pct exec {ct} -- sh -c 'base64 -d > {CFG}.new && "
|
||||||
|
f"cp -a {CFG} {CFG}.presync && mv {CFG}.new {CFG} && "
|
||||||
|
f"systemctl restart {SVC} && sleep 2 && systemctl is-active {SVC}'",
|
||||||
|
inp=b64)
|
||||||
|
if r.returncode or "active" not in r.stdout:
|
||||||
|
sys.exit(f"push {ct} fehlgeschlagen: {r.stdout} {r.stderr}")
|
||||||
|
print(f" {ct} active")
|
||||||
|
print(cmd_check_msg())
|
||||||
|
return cmd_check()
|
||||||
|
|
||||||
|
|
||||||
|
def cmd_check_msg():
|
||||||
|
return "— Verifikation:"
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
cmd = sys.argv[1] if len(sys.argv) > 1 else "check"
|
||||||
|
sys.exit({"check": cmd_check, "push": cmd_push}.get(cmd, cmd_check)())
|
||||||
98
adguard/ha-self-service.md
Normal file
98
adguard/ha-self-service.md
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
# TP23 Stufe 5 (Redesign 2026-09-12) — DAU-tauglicher, personenbezogener DNS-Self-Service
|
||||||
|
|
||||||
|
> 🔴 **Status: Bau-Spec, noch nicht deployt.** Die alte Fassung (single-domain, temp-only,
|
||||||
|
> HA-Neustart bei neuer Domain) war „funktional ein kastriertes Pi-hole" (User-Befund 2026-09-12)
|
||||||
|
> und ist ersetzt. Bau + Abnahme laufen **on-network** (HAOS `10.40.20.105`, AGH `.36/.37`) — beim
|
||||||
|
> Anlegen den **home-assistant-best-practices-Skill** konsultieren und via **ha-mcp** in `.storage`
|
||||||
|
> anlegen (native Helper, `entity_id` statt `device_id`). Diese Datei ist die Repro-/SSoT-Quelle.
|
||||||
|
|
||||||
|
## Ziel (User-Requirements 2026-09-12)
|
||||||
|
Ein **einfaches, klar verständliches** End-User-UI zum Entsperren:
|
||||||
|
- **alles was den Nutzer betrifft, nichts** von anderen / Admin / Power-User;
|
||||||
|
- Policy nur sichtbar, wo sie für den einfachen Nutzer Relevanz hat.
|
||||||
|
|
||||||
|
| # | Requirement | Umsetzung |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | **Pro Person** (Login), Sicht über alle Geräte | HA-User = Person; Person → AGH-Client (Name), dessen `ids` = ihre Geräte-IPs |
|
||||||
|
| 2 | **„zuletzt geblockt" → 1 Klick** | `recent-blocked --client <person>` aus dem AGH-Query-Log, auf die Geräte der Person gefiltert |
|
||||||
|
| 3 | **temporär UND dauerhaft**, Modus umschaltbar | Dauer-Select inkl. „dauerhaft"; `input_boolean` self-permanent ⇄ Anfrage. **Start: self-permanent** |
|
||||||
|
| 4 | **HA-Dashboard, scoped** pro Person | ein View je Person, nur für ihre HA-User-ID sichtbar |
|
||||||
|
|
||||||
|
## Identitätsmodell — AGH-Client je Person ist die SSoT
|
||||||
|
- Pro Person **ein AGH-Client** (`/control/clients`), `name` = Personenname, `ids` = **alle** Geräte-IPs
|
||||||
|
der Person (die per DHCP faktisch statisch sind). Damit ist Person→Gerät **an einem Ort** gepflegt
|
||||||
|
(kein zweites Mapping-File), und Ausnahmen sind stabil bei Gerätewechsel.
|
||||||
|
- Ausnahme-Regel = `@@||domain^$client='Personenname'` (statt globalem `@@||domain^`).
|
||||||
|
🔴 **Vor Scharfschalten on-network verifizieren, dass AGH `$client=` auf `@@`-Regeln wirkt**
|
||||||
|
(s. `agh-exception.py` Kopf). Fällt der Test negativ aus: Fallback = `$client=<ip>|<ip>` mit den
|
||||||
|
Geräte-IPs statt dem Namen (Code kann beides, `scoped_rule()`).
|
||||||
|
- Die bestehenden **VLAN-CIDR-Gruppen** bleiben die Policy-Grenze (Filter an/aus je VLAN);
|
||||||
|
Personen-Clients sind spezifischer und tragen nur die *Ausnahmen*.
|
||||||
|
|
||||||
|
## CLI (deployt nach `/config/agh-exception.py`, wirkt auf `.36`+`.37`)
|
||||||
|
```
|
||||||
|
allow-domain <domain> --client <person> # Ausnahme setzen (temp: HA revertiert; perm: kein Timer)
|
||||||
|
unallow-domain <domain> --client <person> # dieselbe Ausnahme entfernen
|
||||||
|
list-exceptions --client <person> --json # „meine Ausnahmen" (für Übersicht + Entfernen)
|
||||||
|
recent-blocked --client <person> --json # „zuletzt geblockt" (nur Geräte der Person)
|
||||||
|
```
|
||||||
|
`--client` nimmt einen AGH-Client-Namen (→ IPs via `/control/clients`) ODER eine Komma-IP/CIDR-Liste.
|
||||||
|
|
||||||
|
## `configuration.yaml` — `shell_command` (einziger YAML-Teil, Backup `.bak-tp23-*`)
|
||||||
|
Domain **und** Client sind Parameter → **kein HA-Neustart mehr** bei neuer Domain (das war der alte
|
||||||
|
DAU-Killer: pro Domain ein eigener `shell_command`).
|
||||||
|
```yaml
|
||||||
|
shell_command:
|
||||||
|
agh_allow: "python3 /config/agh-exception.py allow-domain '{{ domain }}' --client '{{ client }}'"
|
||||||
|
agh_unallow: "python3 /config/agh-exception.py unallow-domain '{{ domain }}' --client '{{ client }}'"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Helfer / Entities (via ha-mcp in .storage)
|
||||||
|
Pro Person (Beispiel `christoph`) — bei mehreren Personen je einmal, Suffix = Personen-Slug:
|
||||||
|
| Typ | entity_id | Zweck |
|
||||||
|
|---|---|---|
|
||||||
|
| command_line sensor | `sensor.dns_recent_blocked_<slug>` | `recent-blocked --client <person> --json` → `json_attributes: items` (Liste {domain,count,last}); Scan-Intervall ~60 s |
|
||||||
|
| command_line sensor | `sensor.dns_exceptions_<slug>` | `list-exceptions --client <person> --json` → aktive Ausnahmen der Person |
|
||||||
|
| input_select | `input_select.dns_dauer_<slug>` | 1 Stunde / 4 Stunden / **dauerhaft** |
|
||||||
|
| timer | `timer.dns_ausnahme_<slug>` | JIT-Countdown je temporärer Freigabe (restore=false) |
|
||||||
|
| input_boolean | `input_boolean.dns_permanent_ohne_anfrage` | **global**: an = „dauerhaft" wirkt sofort (Start); aus = erzeugt Admin-Genehmigung |
|
||||||
|
| script | `script.dns_freigeben_<slug>` | nimmt `domain` als Feld → `agh_allow`; bei „dauerhaft"+Switch-aus → `todo`/Notification statt Freigabe; sonst Timer starten |
|
||||||
|
| automation | `automation.dns_ablauf_<slug>` | `timer.finished`/`.cancelled` → `agh_unallow`, Aufräumen |
|
||||||
|
|
||||||
|
## Dashboard — ein scoped View je Person
|
||||||
|
- Eigener storage-Dashboard-View, **nur für die HA-User-ID der Person sichtbar** (`visible:` mit
|
||||||
|
der user-id; `require_admin:false`). Kleiner Haushalt → ein View je Person ist praktikabel und
|
||||||
|
umgeht das „Template kennt den aktuellen Betrachter nicht"-Problem.
|
||||||
|
- **Karte 1 „Zuletzt geblockt (antippen zum Freigeben)":** rendert `sensor.dns_recent_blocked_<slug>`
|
||||||
|
Attribut `items`; jede Zeile hat eine `tap_action` → `script.dns_freigeben_<slug>` mit `domain`
|
||||||
|
der Zeile + aktueller `input_select`-Dauer. (Dynamische Per-Zeilen-tap_action braucht i.d.R.
|
||||||
|
`auto-entities` + `template`/`button-card` **oder** ein kleines Custom-List-Template — **beim Bau
|
||||||
|
on-network entscheiden**, das ist der Teil, der Live-Verifikation braucht.)
|
||||||
|
- **Karte 2 „Meine Ausnahmen":** `sensor.dns_exceptions_<slug>` als Liste, je Eintrag Entfernen
|
||||||
|
(`agh_unallow`).
|
||||||
|
- **Karte 3 „Dauer":** `input_select.dns_dauer_<slug>` (inkl. „dauerhaft").
|
||||||
|
- **Kein** globaler Status, keine fremden Clients, keine Policy-Interna. Nur wenn relevant ein
|
||||||
|
kurzer Hinweis („durch den Familien-Filter blockiert").
|
||||||
|
|
||||||
|
## Genehmigungs-Variante (Modus-Switch aus)
|
||||||
|
`input_boolean.dns_permanent_ohne_anfrage = off` → „dauerhaft"-Klick legt statt der Freigabe einen
|
||||||
|
Eintrag in einer **`todo`-Liste** „DNS-Freigaben zu prüfen" an (+ Notification an Christoph). Beim
|
||||||
|
Abhaken führt eine Admin-Automation `agh_allow` ohne Timer aus. Beide Varianten sind gebaut; der
|
||||||
|
Switch entscheidet zur Laufzeit.
|
||||||
|
|
||||||
|
## Alt-Reste, die beim Bau wegfallen/fixen
|
||||||
|
- Single-domain-Flow (`input_text.dns_ausnahme_domain/_aktiv`, `input_button…_starten`,
|
||||||
|
`automation…_starten/_ablauf`) und der `agh_allow_domain`/`_unallow_domain`-**pro-Domain**-Zwang.
|
||||||
|
- Statuskachel-Sensor `binary_sensor.adguard_bf1_status` hing am toten `.16` → auf `.36`/`.37` neu.
|
||||||
|
|
||||||
|
## Abnahme (on-network, = neue echte Stufe-6-Vorbedingung)
|
||||||
|
Mit einem **echten Nicht-Admin-Login**: geblockte Seite → erscheint in *ihrer* Fläche → 1 Klick
|
||||||
|
temporär (revertiert nach Ablauf) **und** 1 Klick dauerhaft (bleibt) → wirkt **nur** für diese
|
||||||
|
Person (Gegentest: anderer Client sieht die Domain weiter geblockt). `.36 == .37`
|
||||||
|
(`agh-sync.py check`). Modus-Switch beide Wege einmal durchgespielt.
|
||||||
|
|
||||||
|
## Verweise
|
||||||
|
- CLI: `adguard/agh-exception.py` (Subcommands + `selftest`). Deploy-Weg unverändert (base64→scp,
|
||||||
|
s. unten). Creds `/config/agh-exception.secrets` (chmod 600).
|
||||||
|
- Redeploy: `base64 < adguard/agh-exception.py | ssh admin@10.40.20.105 "base64 -d | sudo tee /config/agh-exception.py >/dev/null"`.
|
||||||
|
- Personen-Client-Modell: `adguard/README.md`. Kontext/Reihenfolge: TP23 in `docs/teilprojekte.md`.
|
||||||
218
bin/gitea-tp-sync.py
Normal file
218
bin/gitea-tp-sync.py
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
TP36 (reframed) — teilprojekte.md Index/Status → Gitea Issues (admin/bf-infra).
|
||||||
|
|
||||||
|
Idempotenter Sync (get-or-create, Match ueber "TPnn" im Titel):
|
||||||
|
- Archiv-Header in docs/teilprojekte-archiv.md -> closed Issues (nur TPs, die in Gitea
|
||||||
|
NICHT offen sind; ein reopentes TP mit Archiv-Zeiger bleibt offen).
|
||||||
|
- Labels status:* (Marker) + thema:* (Gruppierung) werden vorab get-or-create.
|
||||||
|
|
||||||
|
SSoT-Modell (seit TP36-Cutover 2026-09-07): Gitea fuehrt Index+Status — aktive TPs werden
|
||||||
|
DIREKT als Gitea-Issue angelegt/gepflegt, NICHT mehr aus einer Datei-Tabelle abgeleitet
|
||||||
|
(die Quick-Index-Tabelle wurde aus teilprojekte.md entfernt). Dieses Werkzeug haelt nur noch
|
||||||
|
die ARCHIVIERTEN (closed) TPs mit teilprojekte-archiv.md idempotent abgeglichen und fasst
|
||||||
|
offene Issues nie an. Die per-TP-Prosa bleibt in docs/teilprojekte.md / teilprojekte-archiv.md.
|
||||||
|
Token: ~/.hermes/.env (GITEA_ACCESS_TOKEN, GITEA_HOST) — wird NIE ausgegeben.
|
||||||
|
|
||||||
|
Aufruf:
|
||||||
|
python3 bin/gitea-tp-sync.py # Dry-Run (default) — zeigt, was passieren wuerde
|
||||||
|
python3 bin/gitea-tp-sync.py --commit # legt Labels + Issues an / gleicht sie ab
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import urllib.request
|
||||||
|
import urllib.error
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REPO = Path(__file__).resolve().parent.parent
|
||||||
|
ACTIVE = REPO / "docs" / "teilprojekte.md"
|
||||||
|
ARCHIV = REPO / "docs" / "teilprojekte-archiv.md"
|
||||||
|
HERMES = Path.home() / ".hermes" / ".env"
|
||||||
|
OWNER_REPO = "admin/bf-infra"
|
||||||
|
COMMIT = "--commit" in sys.argv
|
||||||
|
|
||||||
|
# Marker -> (status-label, gitea-state)
|
||||||
|
MARKER = {
|
||||||
|
"🔜": ("status:todo", "open"),
|
||||||
|
"⏳": ("status:wip", "open"),
|
||||||
|
"⏸️": ("status:paused", "open"),
|
||||||
|
"💡": ("status:idea", "open"),
|
||||||
|
"🟢": ("status:concept", "open"),
|
||||||
|
"✅": ("status:done", "closed"),
|
||||||
|
"❌": ("status:dropped", "closed"),
|
||||||
|
}
|
||||||
|
LABEL_COLORS = {
|
||||||
|
"status:todo": "#ededed", "status:wip": "#fbca04", "status:paused": "#c5def5",
|
||||||
|
"status:idea": "#d4c5f9", "status:concept": "#bfdadc", "status:done": "#0e8a16",
|
||||||
|
"status:dropped": "#e11d21",
|
||||||
|
"thema:firewall-dns": "#b60205", "thema:wlan": "#1d76db", "thema:cloudflare-proxy": "#f9a825",
|
||||||
|
"thema:nautobot-doku": "#5319e7", "thema:infra": "#0e8a16", "thema:ipv6-matter": "#006b75",
|
||||||
|
"thema:identity": "#e99695", "thema:backlog": "#cccccc",
|
||||||
|
}
|
||||||
|
# TP-Nummer -> Themen-Label (aus dem Statusbaum-Gruppierung; nur aktive TPs brauchen ein Board-Thema)
|
||||||
|
THEME = {
|
||||||
|
"5.1": "firewall-dns", "23": "firewall-dns",
|
||||||
|
"25": "wlan", "28": "wlan", "13": "wlan", "17.1": "wlan", "17.3": "wlan",
|
||||||
|
"16": "cloudflare-proxy", "16.2": "cloudflare-proxy", "16.3": "cloudflare-proxy",
|
||||||
|
"31": "cloudflare-proxy", "41": "cloudflare-proxy", "14.4": "cloudflare-proxy",
|
||||||
|
"42": "cloudflare-proxy", "43": "cloudflare-proxy", "44": "cloudflare-proxy",
|
||||||
|
"32": "nautobot-doku", "34": "nautobot-doku", "35": "nautobot-doku",
|
||||||
|
"36": "nautobot-doku", "37": "nautobot-doku",
|
||||||
|
"30": "infra", "40": "infra", "20.1": "infra", "18": "infra",
|
||||||
|
"24": "infra", "38": "infra", "11": "infra",
|
||||||
|
"12": "ipv6-matter", "12.1": "ipv6-matter",
|
||||||
|
"8": "identity",
|
||||||
|
"29": "backlog", "33": "backlog",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def hermes_env():
|
||||||
|
tok = host = None
|
||||||
|
for line in HERMES.read_text().splitlines():
|
||||||
|
m = re.match(r"\s*(GITEA_ACCESS_TOKEN|GITEA_HOST)\s*=\s*(.+?)\s*$", line)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
if m.group(1) == "GITEA_ACCESS_TOKEN":
|
||||||
|
tok = m.group(2).strip().strip('"').strip("'")
|
||||||
|
else:
|
||||||
|
host = m.group(2).strip().strip('"').strip("'")
|
||||||
|
if not tok:
|
||||||
|
sys.exit("GITEA_ACCESS_TOKEN nicht in %s gefunden." % HERMES)
|
||||||
|
host = (host or "https://gitea.emsenhuber.net").rstrip("/")
|
||||||
|
if not host.startswith("http"):
|
||||||
|
host = "https://" + host
|
||||||
|
return tok, host
|
||||||
|
|
||||||
|
|
||||||
|
TOKEN, HOST = hermes_env()
|
||||||
|
API = HOST + "/api/v1"
|
||||||
|
|
||||||
|
|
||||||
|
def api(method, path, body=None):
|
||||||
|
url = API + path
|
||||||
|
data = json.dumps(body).encode() if body is not None else None
|
||||||
|
req = urllib.request.Request(url, data=data, method=method, headers={
|
||||||
|
"Authorization": "token " + TOKEN,
|
||||||
|
"Accept": "application/json", "Content-Type": "application/json"})
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=30) as r:
|
||||||
|
return r.status, json.load(r)
|
||||||
|
except urllib.error.HTTPError as e:
|
||||||
|
return e.code, (json.load(e) if e.headers.get("content-type", "").startswith("application/json") else {})
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- parse
|
||||||
|
def parse_archiv(exclude_nums):
|
||||||
|
"""Archiv-Header -> archivierte TPs. exclude_nums = TP-Nummern, die in Gitea OFFEN sind
|
||||||
|
(reopente TPs mit Archiv-Zeiger) -> die bleiben offen, nicht als closed abgleichen."""
|
||||||
|
out = []
|
||||||
|
hdr = re.compile(r"^#{2,4}\s*(TP[\d.\-A-Za-z]+)\s*—\s*(.+?)\s*▸", )
|
||||||
|
seen = set()
|
||||||
|
for line in ARCHIV.read_text().splitlines():
|
||||||
|
m = hdr.match(line)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
tp, title = m.groups()
|
||||||
|
num = tp[2:]
|
||||||
|
if num in exclude_nums or num in seen:
|
||||||
|
continue
|
||||||
|
seen.add(num)
|
||||||
|
short = re.sub(r"\*\*|`", "", title).strip()[:80]
|
||||||
|
out.append({"tp": tp, "num": num, "marker": "✅",
|
||||||
|
"title": short, "date": "", "src": "archiv"})
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------- gitea sync
|
||||||
|
def ensure_labels(existing):
|
||||||
|
by_name = {l["name"]: l for l in existing}
|
||||||
|
for name, color in LABEL_COLORS.items():
|
||||||
|
if name in by_name:
|
||||||
|
continue
|
||||||
|
if COMMIT:
|
||||||
|
api("POST", f"/repos/{OWNER_REPO}/labels",
|
||||||
|
{"name": name, "color": color.lstrip("#")})
|
||||||
|
print(f" [{'+' if COMMIT else 'DRY'}] label {name}")
|
||||||
|
st, cur = api("GET", f"/repos/{OWNER_REPO}/labels?limit=100")
|
||||||
|
return {l["name"]: l["id"] for l in (cur if isinstance(cur, list) else [])}
|
||||||
|
|
||||||
|
|
||||||
|
def all_issues():
|
||||||
|
out, page = [], 1
|
||||||
|
while True:
|
||||||
|
st, d = api("GET", f"/repos/{OWNER_REPO}/issues?state=all&type=issues&limit=50&page={page}")
|
||||||
|
if not isinstance(d, list) or not d:
|
||||||
|
break
|
||||||
|
out += d
|
||||||
|
page += 1
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print(f"Gitea {HOST} repo {OWNER_REPO} mode={'COMMIT' if COMMIT else 'DRY-RUN'}")
|
||||||
|
|
||||||
|
st, existing_labels = api("GET", f"/repos/{OWNER_REPO}/labels?limit=100")
|
||||||
|
label_id = ensure_labels(existing_labels if isinstance(existing_labels, list) else [])
|
||||||
|
|
||||||
|
issues = all_issues()
|
||||||
|
by_tp = {}
|
||||||
|
open_nums = set()
|
||||||
|
for iss in issues:
|
||||||
|
# Titel ist "TPxx — …"; volle ID inkl. Buchstaben/Bindestrich (TP14.2a, TP-R) bis zum " — "
|
||||||
|
m = re.match(r"^(TP[\w.\-]*?)\s+—", iss["title"])
|
||||||
|
if m:
|
||||||
|
by_tp[m.group(1)] = iss
|
||||||
|
if iss.get("state") == "open":
|
||||||
|
open_nums.add(m.group(1)[2:])
|
||||||
|
|
||||||
|
# Aktive TPs sind seit dem TP36-Cutover SSoT direkt in Gitea -> nur das Archiv abgleichen,
|
||||||
|
# und offene (reopente) TPs davon ausschliessen, damit sie nicht faelschlich geschlossen werden.
|
||||||
|
items = parse_archiv(open_nums)
|
||||||
|
print(f"Geparst: {len(items)} archivierte TP (aktive TPs = SSoT in Gitea: "
|
||||||
|
f"{len(open_nums)} offen, nicht angetastet)\n")
|
||||||
|
|
||||||
|
created = updated = 0
|
||||||
|
for it in items:
|
||||||
|
status_label, state = MARKER.get(it["marker"], ("status:todo", "open"))
|
||||||
|
labels = [status_label]
|
||||||
|
th = THEME.get(it["num"])
|
||||||
|
if th:
|
||||||
|
labels.append(f"thema:{th}")
|
||||||
|
title = f"{it['tp']} — {it['title']}"
|
||||||
|
body = (f"Steuerungs-SSoT: Index/Status hier in Gitea; **Prosa/Detail** im Repo unter "
|
||||||
|
f"`docs/{'teilprojekte-archiv.md' if it['src']=='archiv' else 'teilprojekte.md'}` "
|
||||||
|
f"(Abschnitt `{it['tp']}`).\n\n"
|
||||||
|
f"- Status: {it['marker']} ({status_label})\n"
|
||||||
|
+ (f"- Abschluss: {it['date']}\n" if it["date"] else ""))
|
||||||
|
want_ids = [label_id[l] for l in labels if l in label_id]
|
||||||
|
|
||||||
|
cur = by_tp.get(it["tp"])
|
||||||
|
if cur is None:
|
||||||
|
print(f" [{'+' if COMMIT else 'DRY'}] NEW {title} {labels} state={state}")
|
||||||
|
if COMMIT:
|
||||||
|
sc, res = api("POST", f"/repos/{OWNER_REPO}/issues",
|
||||||
|
{"title": title, "body": body, "labels": want_ids})
|
||||||
|
if state == "closed" and isinstance(res, dict) and res.get("number"):
|
||||||
|
api("PATCH", f"/repos/{OWNER_REPO}/issues/{res['number']}", {"state": "closed"})
|
||||||
|
created += 1
|
||||||
|
else:
|
||||||
|
have = {l["name"] for l in cur.get("labels", [])}
|
||||||
|
drift = (have != set(labels)) or (cur.get("state") != state)
|
||||||
|
if drift:
|
||||||
|
print(f" [{'~' if COMMIT else 'DRY'}] UPD #{cur['number']} {title} "
|
||||||
|
f"{sorted(have)}->{labels} state {cur.get('state')}->{state}")
|
||||||
|
if COMMIT:
|
||||||
|
api("PATCH", f"/repos/{OWNER_REPO}/issues/{cur['number']}",
|
||||||
|
{"labels": want_ids, "state": state})
|
||||||
|
updated += 1
|
||||||
|
|
||||||
|
print(f"\n{'Angelegt' if COMMIT else 'Wuerde anlegen'}: {created} · "
|
||||||
|
f"{'Abgeglichen' if COMMIT else 'Wuerde abgleichen'}: {updated}")
|
||||||
|
if not COMMIT:
|
||||||
|
print("Dry-Run — nichts geschrieben. Mit --commit ausfuehren.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -21,6 +21,13 @@
|
|||||||
# OEFFENTLICH (via Cloudflare Tunnel + Access; Clients ohne WARP)
|
# OEFFENTLICH (via Cloudflare Tunnel + Access; Clients ohne WARP)
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
vw.emsenhuber.net {
|
vw.emsenhuber.net {
|
||||||
|
# TP41 T2: /admin nur lokal/WARP-intern (kein CF-Header). Via CF (Header CF-Connecting-IP
|
||||||
|
# gesetzt) -> 404 statt Vault-Admin. Ersetzt die frühere WAF-Edge-Regel (403).
|
||||||
|
@cf_admin {
|
||||||
|
header CF-Connecting-IP *
|
||||||
|
path /admin /admin/*
|
||||||
|
}
|
||||||
|
respond @cf_admin 404
|
||||||
reverse_proxy http://10.40.20.127
|
reverse_proxy http://10.40.20.127
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,8 +46,8 @@ gitea.emsenhuber.net {
|
|||||||
# =========================================================================
|
# =========================================================================
|
||||||
# INTERN (Mesh/LAN; Split-Horizon zeigt diese Namen auf 10.40.20.40)
|
# INTERN (Mesh/LAN; Split-Horizon zeigt diese Namen auf 10.40.20.40)
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
nautobot.emsenhuber.net, nautobot.bf.emsenhuber.net {
|
nautobot.bf.emsenhuber.net {
|
||||||
reverse_proxy http://10.40.20.30:8080
|
reverse_proxy http://10.40.20.154:8080
|
||||||
}
|
}
|
||||||
|
|
||||||
# grafana.emsenhuber.net — 2026-08-25 entfernt: LXC 104 war seit Langem gestoppt und stand
|
# grafana.emsenhuber.net — 2026-08-25 entfernt: LXC 104 war seit Langem gestoppt und stand
|
||||||
@@ -51,49 +58,49 @@ ds-bf.emsenhuber.net {
|
|||||||
reverse_proxy http://10.40.20.10:8080
|
reverse_proxy http://10.40.20.10:8080
|
||||||
}
|
}
|
||||||
|
|
||||||
ccu.emsenhuber.net, ccu.bf.emsenhuber.net {
|
ccu.bf.emsenhuber.net {
|
||||||
reverse_proxy http://10.40.20.232
|
reverse_proxy http://10.40.20.232
|
||||||
}
|
}
|
||||||
|
|
||||||
iobroker.emsenhuber.net, iobroker.bf.emsenhuber.net {
|
iobroker.bf.emsenhuber.net {
|
||||||
reverse_proxy http://10.40.20.107:8081
|
reverse_proxy http://10.40.20.107:8081
|
||||||
}
|
}
|
||||||
|
|
||||||
z2m25.emsenhuber.net, z2m25.bf.emsenhuber.net {
|
z2m25.bf.emsenhuber.net {
|
||||||
reverse_proxy http://10.40.20.106:8080
|
reverse_proxy http://10.40.20.106:8080
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- MCP-Endpunkte: heute spricht ~/.claude.json diese IPs roh --------------
|
# --- MCP-Endpunkte: heute spricht ~/.claude.json diese IPs roh --------------
|
||||||
pve-mcp.emsenhuber.net, pve-mcp.bf.emsenhuber.net {
|
pve-mcp.bf.emsenhuber.net {
|
||||||
reverse_proxy http://10.40.20.173:3000
|
reverse_proxy http://10.40.20.173:3000
|
||||||
}
|
}
|
||||||
|
|
||||||
ha-mcp.emsenhuber.net, ha-mcp.bf.emsenhuber.net {
|
ha-mcp.bf.emsenhuber.net {
|
||||||
reverse_proxy http://10.40.20.105:9583
|
reverse_proxy http://10.40.20.105:9583
|
||||||
}
|
}
|
||||||
|
|
||||||
n8n-mcp.emsenhuber.net, n8n-mcp.bf.emsenhuber.net {
|
n8n-mcp.bf.emsenhuber.net {
|
||||||
reverse_proxy http://10.40.20.167:3001
|
reverse_proxy http://10.40.20.167:3001
|
||||||
}
|
}
|
||||||
|
|
||||||
gitea-mcp.emsenhuber.net, gitea-mcp.bf.emsenhuber.net {
|
gitea-mcp.bf.emsenhuber.net {
|
||||||
reverse_proxy http://10.40.20.167:3000
|
reverse_proxy http://10.40.20.167:3000
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- HTTPS-Backends (Selbstsignat) -----------------------------------------
|
# --- HTTPS-Backends (Selbstsignat) -----------------------------------------
|
||||||
pve.emsenhuber.net, pve.bf.emsenhuber.net {
|
pve.bf.emsenhuber.net {
|
||||||
reverse_proxy https://10.40.20.100:8006 {
|
reverse_proxy https://10.40.20.100:8006 {
|
||||||
import selfsigned
|
import selfsigned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
portainer.emsenhuber.net, portainer.bf.emsenhuber.net {
|
portainer.bf.emsenhuber.net {
|
||||||
reverse_proxy https://10.40.20.126:9443 {
|
reverse_proxy https://10.40.20.126:9443 {
|
||||||
import selfsigned
|
import selfsigned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pbs.emsenhuber.net, pbs.bf.emsenhuber.net {
|
pbs.bf.emsenhuber.net {
|
||||||
reverse_proxy https://10.40.20.135:8007 {
|
reverse_proxy https://10.40.20.135:8007 {
|
||||||
import selfsigned
|
import selfsigned
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ def main():
|
|||||||
continue
|
continue
|
||||||
if host not in caddy:
|
if host not in caddy:
|
||||||
drift.append(f"NUR-CF {host:<32} {tun}: {svc} (in Caddy nicht vorhanden)")
|
drift.append(f"NUR-CF {host:<32} {tun}: {svc} (in Caddy nicht vorhanden)")
|
||||||
elif not svc.startswith(f"http://{CADDY_IP}"):
|
elif not (svc.startswith(f"http://{CADDY_IP}") or svc.startswith(f"https://{CADDY_IP}")):
|
||||||
drift.append(f"ABWEICHEND {host:<32} CF={svc} (zeigt nicht auf Caddy {CADDY_IP})")
|
drift.append(f"ABWEICHEND {host:<32} CF={svc} (zeigt nicht auf Caddy {CADDY_IP})")
|
||||||
# Caddy-Sites ohne CF-Ingress: nur die suffixlosen Namen sind potenziell Drift.
|
# Caddy-Sites ohne CF-Ingress: nur die suffixlosen Namen sind potenziell Drift.
|
||||||
# *.bf.*-Namen sind bewusst intern (Split-Horizon) und brauchen keinen Ingress.
|
# *.bf.*-Namen sind bewusst intern (Split-Horizon) und brauchen keinen Ingress.
|
||||||
|
|||||||
@@ -19,12 +19,35 @@ if [ -z "${TOKEN}" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# 🔴 `pct push` meldet Exit 0 AUCH wenn es die Quelle nicht lesen kann
|
||||||
|
# ("failed to open <src> for reading", rc=0 — live gemessen 2026-09-21).
|
||||||
|
# Ein `&&`/`||` hinter dem Push beweist deshalb NICHTS, und die anschliessende
|
||||||
|
# `caddy validate` validiert dann die ALTE Datei -> gruene Abnahme bei
|
||||||
|
# unveraendertem Zustand. Genau so ist der Nautobot-Umzug am 2026-09-21
|
||||||
|
# scheinbar gelandet, obwohl live noch die alte Backend-IP stand.
|
||||||
|
# Deshalb: Push + md5-Vergleich in EINEM Remote-Aufruf, der bei Abweichung
|
||||||
|
# hart abbricht. Gepusht wird nur der lokal bekannte Pfad — die frueheren
|
||||||
|
# `\$\$`/`$$`-Verrenkungen waren der Ausloeser.
|
||||||
|
push_verified() { # push_verified <quelle-auf-pve> <ziel-im-ct> <perms> <label>
|
||||||
|
local src=$1 dst=$2 perms=$3 label=$4
|
||||||
|
ssh "${PVE}" "set -e
|
||||||
|
pct push ${CTID} '${src}' '${dst}' --perms ${perms}
|
||||||
|
want=\$(md5sum '${src}' | cut -d' ' -f1)
|
||||||
|
got=\$(pct exec ${CTID} -- md5sum '${dst}' | cut -d' ' -f1)
|
||||||
|
rm -f '${src}'
|
||||||
|
if [ \"\$want\" != \"\$got\" ]; then
|
||||||
|
echo \"FEHLER: ${label} nicht gelandet (\$want != \$got)\" >&2; exit 1
|
||||||
|
fi
|
||||||
|
echo \" ${label} verifiziert (\$got)\""
|
||||||
|
}
|
||||||
|
|
||||||
echo "-> Caddyfile"
|
echo "-> Caddyfile"
|
||||||
scp -q "${HERE}/Caddyfile" "${PVE}:/tmp/.caddyfile.$$"
|
scp -q "${HERE}/Caddyfile" "${PVE}:/tmp/.caddyfile.$$"
|
||||||
ssh "${PVE}" "pct push ${CTID} /tmp/.caddyfile.\$\$ /etc/caddy/Caddyfile --perms 644 2>/dev/null || pct push ${CTID} /tmp/.caddyfile.$$ /etc/caddy/Caddyfile --perms 644; rm -f /tmp/.caddyfile.$$"
|
push_verified "/tmp/.caddyfile.$$" "/etc/caddy/Caddyfile" 644 "Caddyfile"
|
||||||
|
|
||||||
echo "-> Token (maskiert: Laenge ${#TOKEN})"
|
echo "-> Token (maskiert: Laenge ${#TOKEN})"
|
||||||
printf 'CF_DNS_API_TOKEN=%s\n' "${TOKEN}" | ssh "${PVE}" "cat > /tmp/.cenv.$$ && chmod 600 /tmp/.cenv.$$ && pct push ${CTID} /tmp/.cenv.$$ /etc/caddy/caddy.env --perms 600 && rm -f /tmp/.cenv.$$"
|
printf 'CF_DNS_API_TOKEN=%s\n' "${TOKEN}" | ssh "${PVE}" "cat > /tmp/.cenv.$$ && chmod 600 /tmp/.cenv.$$"
|
||||||
|
push_verified "/tmp/.cenv.$$" "/etc/caddy/caddy.env" 600 "caddy.env"
|
||||||
|
|
||||||
echo "-> validate"
|
echo "-> validate"
|
||||||
ssh "${PVE}" "pct exec ${CTID} -- sh -c 'set -a; . /etc/caddy/caddy.env; set +a; /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile' 2>&1 | tail -3"
|
ssh "${PVE}" "pct exec ${CTID} -- sh -c 'set -a; . /etc/caddy/caddy.env; set +a; /usr/local/bin/caddy validate --config /etc/caddy/Caddyfile' 2>&1 | tail -3"
|
||||||
|
|||||||
47
docs/announcements.md
Normal file
47
docs/announcements.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Announcements — bf-infra
|
||||||
|
|
||||||
|
Breaking News / Querschnitts-Änderungen, die **jede** folgende Session kennen muss, bevor sie
|
||||||
|
losarbeitet — Dinge, die man sonst erst nach dem ersten Fehlschlag merkt. Kein Ersatz für die
|
||||||
|
TP-Prosa (`docs/teilprojekte.md`) oder das Gitea-Board; hier steht nur, was *quer* zu den TPs gilt.
|
||||||
|
|
||||||
|
**Format je Eintrag:** Überschrift `## YYYY-MM-DD — <Titel>`, dann die Zeilen
|
||||||
|
**Betrifft:** (wer/was) und **Aktion:** (was eine Session konkret anders machen muss),
|
||||||
|
optional ein, zwei Sätze Kontext. **Neueste oben.** Der Skill `/news` liest diese Datei und
|
||||||
|
zeigt die Einträge, die seit dem letzten `/news`-Lauf dazugekommen sind (Rest auf Wunsch).
|
||||||
|
|
||||||
|
<!-- TP35 Schicht 4. Eintrag hinzufügen = neuen ##-Block ganz oben unter dieser Zeile einfügen. -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2026-09-12 — Golden-Config-Backups laufen jetzt täglich (vorher KEIN Scheduler)
|
||||||
|
**Betrifft:** alle, die sich auf GC-Backups/Compliance oder `gc-snapshot.py`-Werte verlassen.
|
||||||
|
**Aktion:** Es gab **nie** einen automatischen GC-Backup-Zeitplan — Backup/Intended/Compliance liefen
|
||||||
|
nur manuell aus Sessions (zuletzt 09-04 → 8 Tage Stillstand). Jetzt ScheduledJob **„GC Backup+Compliance
|
||||||
|
– taeglich"** (Nautobot, *Execute All GC Jobs - Multiple Device*, `platform=RouterOS`, täglich 03:30 UTC,
|
||||||
|
Reihenfolge Backup→Intended→Compliance). Wenn `backup_last_success_date` eines Geräts alt ist: zuerst
|
||||||
|
prüfen, ob der ScheduledJob noch `enabled` ist (`/api/extras/scheduled-jobs/`), nicht das Gerät verdächtigen.
|
||||||
|
|
||||||
|
## 2026-09-12 — claude.json-Secrets laufen über `{env:}` (TP35 Schicht 2 ✅)
|
||||||
|
**Betrifft:** alle, die `~/.claude.json` oder `~/.secrets/exports.zsh` anfassen.
|
||||||
|
**Aktion:** Keine Klartext-Secrets mehr in `~/.claude.json` — MCP-Credentials referenzieren jetzt
|
||||||
|
`{env:MCP_*}` (dieselben Namen wie `opencode.jsonc`, bidirektional kompatibel). SSoT ist
|
||||||
|
`~/.secrets/exports.zsh` (25 Vars). Nach jeder Änderung dort: `~/dev/.bin/gui-env-sync.sh`
|
||||||
|
**und App-Neustart** (laufender Prozess erbt die launchd-Umgebung nicht). ⚠ `~/.claude.json` nie aus
|
||||||
|
einer **laufenden** Session editieren — Claude Code schreibt dort State, der Edit würde überschrieben;
|
||||||
|
der Flip läuft bei beendeter App über `~/dev/.bin/schicht2-claude-json-migrate.py --apply-claude`.
|
||||||
|
|
||||||
|
## 2026-09-12 — TP36-Live: Gerätedaten stehen als CustomFields in Nautobot
|
||||||
|
**Betrifft:** alle, die Geräte-Ist-Werte (RouterOS-Version, Compliance %, DHCP-Server-Zahl,
|
||||||
|
DNS-Modus, Mgmt-IP) brauchen.
|
||||||
|
**Aktion:** Nicht mehr aus der Prosa ablesen und nicht raten — die fünf Felder (`grouping: TP36-Live`)
|
||||||
|
hängen an den 5 RouterOS-`dcim.Device` in Nautobot. Aktualisieren: `python3 nautobot/gc-snapshot.py
|
||||||
|
--commit` (liest nur aus Nautobot/GC, kontaktiert kein Gerät). Die Werte sind so aktuell wie das
|
||||||
|
letzte Golden-Config-Backup (`backup_last_success_date` am Gerät).
|
||||||
|
|
||||||
|
## 2026-09-07 — SSoT-Cutover: Index/Status = Gitea, NICHT mehr teilprojekte.md
|
||||||
|
**Betrifft:** jede Session, die einen TP-Status ändert, anlegt oder abschließt.
|
||||||
|
**Aktion:** Status/Reihenfolge/„was lief zuletzt" gehen ins **Gitea-Issue**
|
||||||
|
([`admin/bf-infra`](https://gitea.emsenhuber.net/admin/bf-infra/issues), Labels `status:*`/`thema:*`),
|
||||||
|
**nicht** in eine Tabelle in `docs/teilprojekte.md`. Die Datei trägt nur noch die `### TPnn`-Prosa.
|
||||||
|
`/projektstatus` liest Baum + Status aus der Gitea-API. Branch `tpNN-kurz` jederzeit möglich, Merge
|
||||||
|
nach `main`, sobald Code/Config steht.
|
||||||
@@ -1,8 +1,40 @@
|
|||||||
# Cloudflare-Tunnel — Mapping & #1033-Diagnose (Single Source of Truth)
|
# Cloudflare-Tunnel — Mapping & #1033-Diagnose (Single Source of Truth)
|
||||||
|
|
||||||
> **Aktueller Stand: → §10 (2026-07-10, TP16 UMGESETZT ✅).** §9 = Analyse/Zielbild, §10 = As-Built.
|
> 🆕 **SSoT-Wechsel im Aufbau (TP44, seit 2026-09-02):** Die CF-Estate (Tunnel, WARP-Connector,
|
||||||
> Abschnitte 1–8 sind Historie (Track A, 2026-06-30); die „3 Connectors gut/Zielbild"-Aussagen dort sind
|
> VPC-Services, Hyperdrive, Workers, Access-Apps) wird als **native `nautobot.cloud`-Objekte** in
|
||||||
> durch §9/§10 **überholt** (Round-Robin = macvlan-Host-Isolation → 1/N-Ausfälle). Track A aus `docs/handoff-cf-tunnel-1033.md`.
|
> Nautobot (`10.40.20.154`, TP45 Schicht A 2026-09-21; vorher `.30`) geführt — Etappe 1 (manuelle Erstbefüllung) ✅. Seeder:
|
||||||
|
> `nautobot/tp44-cf-estate-seed.py`, Detail: `docs/handoff-20260902-cf-estate-nautobot-ssot.md`.
|
||||||
|
> **CF bleibt operativer Master, Nautobot = Dokumentations-SSoT.** Dieses File bleibt gültige Historie
|
||||||
|
> und As-Built-Log; die *strukturierte* Estate liest man ab jetzt in Nautobot. Etappe 2 (CF-API→Nautobot-Sync)
|
||||||
|
> ist offen.
|
||||||
|
|
||||||
|
> **Aktueller Stand: → §0 (unten).** §1–16 sind Historie/As-Built-Log früherer Sessions und beschreiben
|
||||||
|
> Topologien, die **nicht mehr live** sind (v.a. das 3-Tunnel-HA `bf-mm`/`bf-nas`/`bf-pve`). §9 = Analyse,
|
||||||
|
> §10–15 = As-Built des inzwischen abgelösten 3-Tunnel-Designs. Track A aus `docs/handoff-cf-tunnel-1033.md`.
|
||||||
|
|
||||||
|
## §0 — Live-Ist-Stand (2026-09-08, maßgeblich)
|
||||||
|
|
||||||
|
> Der einzige aktuelle Topologie-Stand. **§1–16 nicht als aktuelle Konfiguration lesen** — insbesondere das
|
||||||
|
> 3-Tunnel-HA `bf-mm`/`bf-nas`/`bf-pve` (§10–15) existiert seit dem TP16.3-Cutover (bf-public/Caddy,
|
||||||
|
> 2026-08-27) **nicht mehr**. Strukturierte Estate = Nautobot (TP44, deckt sich mit diesem Stand);
|
||||||
|
> operativer Master = Cloudflare. Read-only gemessen gegen die CF-API (`cfd_tunnel`, `warp_connector`,
|
||||||
|
> `teamnet/routes`) am 2026-09-08.
|
||||||
|
|
||||||
|
**cloudflared-Tunnel (live) — es gibt genau drei:**
|
||||||
|
|
||||||
|
| Tunnel | ID | Conns | Zweck |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **bf-public** | `48d3b0b6-7233-4781-a76a-1de4a5e957ce` | 2, healthy | **einziger öffentlicher BF-Tunnel** (TP31/TP16.3), erstellt 2026-08-27 |
|
||||||
|
| **fm.emsenhuber.net** | `b697d124-53bf-4a33-8fcf-384cced4db1a` | 1, healthy | Standort FM (`ds-fm`, `gateway-fm`), separat |
|
||||||
|
| **terpene-db** | `f2d2fc8a-54ae-41df-b09a-97312cdb9fe2` | 1, healthy | DB-Pfad (Hyperdrive→VPC-Service), Rebuild 2026-09-01 — s. §17 |
|
||||||
|
|
||||||
|
**bf-public-Ingress** → alle vier auf **Caddy `10.40.20.40:443`** (Caddy macht das Fan-out), catch-all 404:
|
||||||
|
`vw` · `hass` · `n8n` · `gitea` (`.emsenhuber.net`).
|
||||||
|
|
||||||
|
**WARP / Private Access — nativer Mesh, KEIN cloudflared:**
|
||||||
|
- warp_connector **bf-core-mesh** `d1da7c8a-2736-4cbb-8af1-32db5393fb9c`, healthy — nativer
|
||||||
|
WireGuard-Mesh-Node auf dem Core (TP16.1). **Live und produktiv** (nicht mehr „gated/Konzept" wie in §14).
|
||||||
|
- Routen: `10.40.0.0/16` → **bf-core-mesh** · `10.101.0.0/24` → **fm**. Ein vnet `vnetemsenhuber` (default).
|
||||||
|
|
||||||
## 1. Alle Tunnel im Account (`393c…66c36`)
|
## 1. Alle Tunnel im Account (`393c…66c36`)
|
||||||
| Tunnel | ID | Status | Conns | Verwendung |
|
| Tunnel | ID | Status | Conns | Verwendung |
|
||||||
@@ -226,6 +258,9 @@ Der macmini ist vollwertiger dritter Knoten (nicht „vergessen").
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>🗄️ <b>ARCHIV (§10–16)</b> — überholtes 3-Tunnel-HA <code>bf-mm</code>/<code>bf-nas</code>/<code>bf-pve</code>. Seit dem TP16.3-Cutover (2026-08-27) nicht mehr live — siehe §0. Aufklappen nur für die Historie.</summary>
|
||||||
|
|
||||||
## 10. AS-BUILT 2026-07-10 (TP16 umgesetzt) — reziprokes 3-Tunnel-HA ✅
|
## 10. AS-BUILT 2026-07-10 (TP16 umgesetzt) — reziprokes 3-Tunnel-HA ✅
|
||||||
> Gebaut & verifiziert in eigener Session. Round-Robin-#1033 **strukturell weg** (Loop-Gegentest 0×530 über alle 19 Live-FQDNs, HA per Host-Ausfall-Sim bewiesen). Schritte 1–4+HA durch; **Schritt 5 (Access/WARP + `vw`) offen** (user-gated).
|
> Gebaut & verifiziert in eigener Session. Round-Robin-#1033 **strukturell weg** (Loop-Gegentest 0×530 über alle 19 Live-FQDNs, HA per Host-Ausfall-Sim bewiesen). Schritte 1–4+HA durch; **Schritt 5 (Access/WARP + `vw`) offen** (user-gated).
|
||||||
|
|
||||||
@@ -329,6 +364,10 @@ Letzte in §9.2/§8 als „bleibt `.41`" geführte Origin-Klasse (`pve .41.100`,
|
|||||||
|
|
||||||
## 14. Richtungsentscheid 2026-07-16 — Private Access via native Cloudflare Mesh (WARP Connector) auf dem CRS326 Core-Switch (TP16.1)
|
## 14. Richtungsentscheid 2026-07-16 — Private Access via native Cloudflare Mesh (WARP Connector) auf dem CRS326 Core-Switch (TP16.1)
|
||||||
|
|
||||||
|
> ✅ **Nachtrag 2026-09-08 (live):** umgesetzt. warp_connector **bf-core-mesh** (`d1da7c8a-…`) ist healthy
|
||||||
|
> und trägt die Route `10.40.0.0/16` — der „Scharfschalten = eigene Session, gated"-Status unten ist
|
||||||
|
> überholt. Siehe §0.
|
||||||
|
|
||||||
> ✅ **Update 2026-07-22 (User-Entscheid):** Ziel-Device retargeted von **ap1** auf den **CRS326 Core-Switch** — konsistent mit TP15-Endstand (kein RB5009, Router bleibt ap1/ap2) und TP21s Kabel-Fabric-Modell. Reine Ziel-Doku-Änderung, Scharfschalten bleibt gated bis TP21 den CRS326 abdeckt. Der Rest dieser Sektion ist entsprechend auf den neuen Zielhost umgeschrieben; CRS326 ist aktuell noch Werkszustand, Management-IP/Interface TBD.
|
> ✅ **Update 2026-07-22 (User-Entscheid):** Ziel-Device retargeted von **ap1** auf den **CRS326 Core-Switch** — konsistent mit TP15-Endstand (kein RB5009, Router bleibt ap1/ap2) und TP21s Kabel-Fabric-Modell. Reine Ziel-Doku-Änderung, Scharfschalten bleibt gated bis TP21 den CRS326 abdeckt. Der Rest dieser Sektion ist entsprechend auf den neuen Zielhost umgeschrieben; CRS326 ist aktuell noch Werkszustand, Management-IP/Interface TBD.
|
||||||
|
|
||||||
**Trennung der Concerns (wichtig):**
|
**Trennung der Concerns (wichtig):**
|
||||||
@@ -443,3 +482,42 @@ SSoT und hat eine ganze Architektur getragen.
|
|||||||
Netzwerk-Namespace, sieht ohnehin alles), und die cloudflared-Connectoren laufen auf **ap1/ap2** —
|
Netzwerk-Namespace, sieht ohnehin alles), und die cloudflared-Connectoren laufen auf **ap1/ap2** —
|
||||||
eigene Maschinen, auf denen die Blindheit prinzipiell nicht auftreten kann. Der Shim bleibt als Option
|
eigene Maschinen, auf denen die Blindheit prinzipiell nicht auftreten kann. Der Shim bleibt als Option
|
||||||
für den Fall, dass später doch ein Dienst neben macvlan-Nachbarn einziehen soll.
|
für den Fall, dass später doch ein Dienst neben macvlan-Nachbarn einziehen soll.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## 17. Terpenfinder-DB-Pfad neu aufgebaut 2026-09-02 (Fremdprojekt-Handout blaueblüten)
|
||||||
|
|
||||||
|
**Auslöser:** Handout `blaueblueten-n8n/docs/handout-bf-infra-db-pfad-20260902.md` — der Cloudflare-Worker
|
||||||
|
`terpene` (`420.teilzeitladen.xyz`) lieferte `503 CONNECTION_CLOSED …hyperdrive.local:5432` auf allen
|
||||||
|
`/api/*`-Reads.
|
||||||
|
|
||||||
|
**Root-Cause:** TP16.3 §6 hat beim Alttunnel-Abbau den Container **`terpene-cloudflared` (LXC 400)** mit
|
||||||
|
abgeräumt, obwohl das Runbook ihn ausdrücklich als „nicht anfassen" markierte. Damit war der Tunnel
|
||||||
|
`terpene-db` (`b83b68f0`) `deleted_at 2026-08-31`. Der **VPC-Service** `terpene-db` (`019fa3d5`) zeigte
|
||||||
|
weiter auf diese tote ID. Der Public-Weg (PWA→`bf-public`/Caddy) und der **DB-Weg**
|
||||||
|
(Hyperdrive→VPC-Service→cloudflared→Postgres) wurden verwechselt — der DB-Weg ist unabhängig und blieb
|
||||||
|
ungesichert.
|
||||||
|
|
||||||
|
**Verworfene Optionen (live geprüft):**
|
||||||
|
- **Mesh (`bf-core-mesh`, warp_connector):** CF lehnt ab — `5101: Tunnel is not a cloudflared tunnel`.
|
||||||
|
Ein VPC-Service akzeptiert im `host.network.tunnel_id` **nur einen cloudflared-Tunnel**, keinen
|
||||||
|
warp_connector. (Mesh geht nur über *VPC Networks*, die Hyperdrive nicht nutzt.)
|
||||||
|
- **`bf-public` (AP-Endpoints) wiederverwenden:** bräuchte eine neue Firewallregel `vlan99→10.40.20.20:5432`
|
||||||
|
(Loch mgmt→INFRA, heute nur `.20.40:443` offen, Regel `*42`) **plus** `warp-routing` auf dem geteilten
|
||||||
|
Public-Tunnel (steht auf `false`) — Projekt-Vermischung. Verworfen.
|
||||||
|
|
||||||
|
**Umgesetzt (Option B):** eigener neuer cloudflared-Tunnel neben der DB.
|
||||||
|
|
||||||
|
| Objekt | Wert |
|
||||||
|
|---|---|
|
||||||
|
| cfd_tunnel `terpene-db` (neu) | **`f2d2fc8a-54ae-41df-b09a-97312cdb9fe2`**, remotely-managed, healthy/4 Conns |
|
||||||
|
| Connector | Container `terpene-cloudflared` auf **LXC 400** (VLAN20, `.20.126`), `restart=unless-stopped`, Token per `--env-file` |
|
||||||
|
| VPC-Service `terpene-db` | `019fa3d5-…` `host.network.tunnel_id` → `f2d2fc8a` (PUT 200, 2026-09-01T23:57Z) |
|
||||||
|
| Firewall | **0 Änderungen** — Connector und DB im selben VLAN20, kein Cross-VLAN |
|
||||||
|
| Tombstone | Alttunnel `b83b68f0` bleibt soft-deleted, kein Aufräumbedarf |
|
||||||
|
|
||||||
|
🔑 **Gotcha 1:** VPC-Service = `/accounts/{id}/connectivity/directory/services/{id}`, Update ist **PUT**
|
||||||
|
(voller Body), **nicht** unter `/vpc/…`. 🔑 **Gotcha 2:** der Repo-`CF_API_TOKEN` (`mcp/cloudflare/.env`)
|
||||||
|
darf `cfd_tunnel` (Create lief per curl), aber **nicht** die Workers-VPC-Resource — dort `10000 Auth error`;
|
||||||
|
der PUT lief über den `cloudflare`-MCP-Credential. 🔑 **Merkregel:** vor einem AP-basierten Weg immer die
|
||||||
|
Egress-VLAN-Seite der Connectoren prüfen — `bf-public` sitzt auf **vlan99**, nicht vlan20.
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ terpene.teilzeitladen.xyz {
|
|||||||
"type": "http",
|
"type": "http",
|
||||||
"url": "https://n8n-mcp.emsenhuber.net/mcp",
|
"url": "https://n8n-mcp.emsenhuber.net/mcp",
|
||||||
"headers": {
|
"headers": {
|
||||||
"Authorization": "Bearer 47a34f5589388d37d8b7503abac07b930df264c469ef0674aaeeea9b71dde847"
|
"Authorization": "Bearer <redacted>"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
69
docs/handoff-20260901-n8n-mcp-ssrf.md
Normal file
69
docs/handoff-20260901-n8n-mcp-ssrf.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
# Handoff 2026-09-01 — n8n-mcp SSRF-Block ✅ GEFIXT + in Portainer-Stack 174 gefaltet
|
||||||
|
|
||||||
|
## Kontext
|
||||||
|
Aus TP10 (Terpenfinder Auto-Refresh, Repo `blaueblueten-n8n`) ausgelagerter bf-infra-Task:
|
||||||
|
der n8n-MCP war einsatzfähig, aber seine Anbindung an die **n8n-API** war blockiert. Diese
|
||||||
|
Session hat den Blocker behoben und die Container-Definition sauber verortet.
|
||||||
|
|
||||||
|
## Befund (Ausgangslage)
|
||||||
|
- **Netz war nie das Problem:** aus dem `n8n-mcp`-Container erreichten **beide** Wege n8n —
|
||||||
|
intern `http://n8n:5678` → HTTP 200, öffentlich `https://n8n.emsenhuber.net/healthz` → HTTP 200.
|
||||||
|
- **Einziger Blocker = App-eigener SSRF-Guard** von n8n-mcp (v2.57.1 → nach Redeploy 2.77.0):
|
||||||
|
`N8N_API_URL=http://n8n:5678` löst auf die private Docker-Bridge-IP `172.19.0.3` auf, und der
|
||||||
|
Guard lehnte private IPs im Default-Modus `strict` ab (`SSRF protection: Private IP addresses
|
||||||
|
not allowed`, `connected:false`). Der API-Client validiert `N8N_API_URL` über genau diesen
|
||||||
|
Guard (`/app/dist/services/n8n-api-client.js:104` → `SSRFProtection.validateWebhookUrl`).
|
||||||
|
- **SSoT-Drift (mit-gefixt):** der laufende Container war ein **Waise** aus
|
||||||
|
`~/dev/homelab-cline/n8n-mcp-deploy/docker-compose.yml` (kein Git-Repo), und dieses File stimmte
|
||||||
|
mit dem laufenden Container nicht überein (File: `https://n8n.emsenhuber.net` + Netz `web`;
|
||||||
|
Container: `http://n8n:5678` + Netz `n8n_default`). `n8n`+`n8n-redis` liefen dagegen im
|
||||||
|
Portainer-Stack **174** (`n8n`, Endpoint 20 = macmini).
|
||||||
|
|
||||||
|
## Fix (durchgeführt 2026-09-01)
|
||||||
|
Entscheidung (User): **intern + permissive**, und **n8n-mcp in Portainer-Stack 174 falten**.
|
||||||
|
|
||||||
|
1. **SSRF entschärft:** `WEBHOOK_SECURITY_MODE=permissive` gesetzt → private/LAN-IP für
|
||||||
|
`N8N_API_URL=http://n8n:5678` erlaubt. Bleibt auf der Docker-Bridge, **kein** CF/Tunnel/Caddy-
|
||||||
|
Umweg, keine 10-s-DNS-Falle, unabhängig von der Edge. Vertretbar, weil dieser Container
|
||||||
|
ausschließlich mit dem trusted LAN-n8n redet.
|
||||||
|
2. **Waise beseitigt:** orphan-Container entfernt (`docker rm -f n8n-mcp`), n8n-mcp als **dritter
|
||||||
|
Service** in Stack 174 aufgenommen (neben `n8n`+`redis`, gemeinsames Default-Netz `n8n_default`,
|
||||||
|
`depends_on: n8n`). Stack via Portainer-API redeployt (`PUT /api/stacks/174?endpointId=20`,
|
||||||
|
`prune:false`).
|
||||||
|
3. **Erhalten geblieben:** Bearer (`AUTH_TOKEN`==`MCP_AUTH_TOKEN`, len 64) **unverändert** →
|
||||||
|
bestehende Clients (opencode `http://10.40.20.167:3001/mcp`) laufen weiter; Host-Port-Bind
|
||||||
|
`10.40.20.167:3001:3000` → Caddy-Route `n8n-mcp.emsenhuber.net` (Caddyfile Z. 75) intakt;
|
||||||
|
Daten-Volume als **`external`** wiederverwendet (`n8n-mcp-deploy_n8n-mcp-data`, `nodes.db`
|
||||||
|
bleibt) — eigener `N8N_API_KEY` von n8n-mcp (≠ n8n) unverändert.
|
||||||
|
|
||||||
|
## Verifikation (nicht behauptet — gemessen)
|
||||||
|
- `SSRFProtection.validateWebhookUrl("http://n8n:5678")` im Container → `valid:true`,
|
||||||
|
`resolved:172.19.0.3`, `mode:permissive`.
|
||||||
|
- MCP-Tool `n8n_health_check` (voller Handshake) → `{"success":true,"status":"ok",
|
||||||
|
"apiUrl":"http://n8n:5678","mcpVersion":"2.77.0","upToDate":true}`.
|
||||||
|
- `n8n`+`n8n-redis` behielten Container-ID **und** `StartedAt` (2026-07-29) → **kein n8n-Downtime**
|
||||||
|
beim Redeploy (nur der neue Service wurde erstellt).
|
||||||
|
|
||||||
|
## SSoT / Ablage
|
||||||
|
- **Master der Stack-Definition = Portainer-Stack 174** (`n8n`, Endpoint 20, macmini).
|
||||||
|
- Gitignored Repo-Mirror + Rollback: `docs/macmini-vlan20/stacks/174-n8n.yml` (neuer Stand) und
|
||||||
|
`…/174-n8n.orig-pre-mcp-fold-20260901.yml` (Vorzustand). Neu gitignored: `docs/macmini-vlan20/stacks`.
|
||||||
|
- INVENTORY-Eintrag `mcp/INVENTORY.md` (n8n-mcp) aktualisiert.
|
||||||
|
|
||||||
|
## Offene Punkte
|
||||||
|
- 🔴 **TP30 Posten #8:** beim Ausgeben des Stack-174-Files ist `N8N_SMTP_PASS` (Gmail-App-PW
|
||||||
|
`johnny.claw.0x00@gmail.com`) ins Transkript geraten (Redaktionsmuster hatte `PASSWORD`, nicht
|
||||||
|
bare `PASS`). **Rotation fällig** — Google App-Passwort widerrufen + Stack-174-Env nachziehen.
|
||||||
|
- ⚠️ **Toter Orphan:** `~/dev/homelab-cline/n8n-mcp-deploy/` (kein Git) kann gelöscht werden — ein
|
||||||
|
erneutes `docker compose up` dort würde sonst einen Namenskonflikt mit dem Stack-174-Container
|
||||||
|
erzeugen. Nicht kritisch (nur bei Handstart).
|
||||||
|
- ⚠️ **Client-401 in THIS Claude-Code-Session:** der n8n-mcp-Server kam in dieser Session mit
|
||||||
|
`AUTH_HEADER_REJECTED` (401) hoch — das ist ein **Client-Config-Thema** (`~/.claude.json`-Bearer
|
||||||
|
leer/falsch, vgl. globale CLAUDE.md „kein Shell → `{env:}` leer"), **nicht** der hier gefixte
|
||||||
|
Server. Der Server antwortet LAN-direkt korrekt; opencode ist unbetroffen.
|
||||||
|
|
||||||
|
## Auswirkung auf TP10
|
||||||
|
Keine für den Feature-Pfad: TP10 läuft über **Cloudflare Worker Cron**, nicht über n8n. Der Fix
|
||||||
|
macht n8n-mcp aber wieder als **Werkzeug** nutzbar (n8n-Workflow-Validierung, künftig optional
|
||||||
|
TP10-als-n8n-Workflow). Rückantwort ans zuweisende Projekt:
|
||||||
|
`~/dev/blaueblueten-n8n/docs/rueckantwort-n8n-mcp-fix-20260901.md`.
|
||||||
109
docs/handoff-20260901-tp41-followup.md
Normal file
109
docs/handoff-20260901-tp41-followup.md
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# Handoff 2026-09-01 — TP41 Follow-up: IdP-Feinschliff, App-Launcher, Geli-Zugänge, dw-DNS
|
||||||
|
|
||||||
|
> Kleine Folgesession nach TP41 (T4/T3/T2 durch). Sammelt die Rest-Punkte rund um **Logins für Geli**,
|
||||||
|
> die **App-Launcher-Seite** und den **dw-Split-Horizon** für Terpenfinder. Mehrere Punkte sind
|
||||||
|
> **User-Konsolen-Aufgaben** (Google/Meta), dafür je eine Kurzanleitung.
|
||||||
|
|
||||||
|
## Status je Punkt (Stand dieser Session)
|
||||||
|
| # | Punkt | Stand |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | GitHub-Login | ✅ funktioniert (User bestätigt) |
|
||||||
|
| 2 | Google „Zugriff blockiert: nur innerhalb der Organisation" | ✅ **gefixt** — User hat Consent-Screen auf *External* gestellt; Google-Login läuft (bestätigt 2026-09-01). Anleitung §A war der Weg. |
|
||||||
|
| 3 | Meta/Facebook einbinden | ❌ **für TP41 abgesagt** (User-Entscheid 2026-09-01) — optional, kein Bedarf. Keine App, kein CF-IdP angelegt. Geli via OTP+Google voll gedeckt. §B nur noch als Anleitung, falls später gewünscht. |
|
||||||
|
| 4 | Geli-Zugänge hinterlegen | ✅ `pillepalle670@gmail.com` in `zt-humans`. **FB-Konto-Mail = `angelikahy@outlook.de`** (schon drin → Facebook automatisch abgedeckt, sobald Meta läuft). Instagram = kein CF-IdP. |
|
||||||
|
| 5 | App-Launcher überarbeiten | ✅ **D1 umgesetzt** — 3 Bookmark-Kacheln (Vaultwarden/Home Assistant/Terpenfinder), Bundle-/Wildcard-/Terpene-self_hosted-Kacheln aus dem Launcher genommen (Policies **unverändert** geprüft), Launcher gestylt (Titel „BF-Zugänge", Begrüßung, Buttons). |
|
||||||
|
| 6 | `420.teilzeitladen.xyz` an dw | ✅ **Entscheidung:** Geli sofort per **WARP**; das dw-Site-DNS-Uplink-Thema → **TP43** (angelegt). |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## §A — Google-Login-Fix (User, Google Cloud Console)
|
||||||
|
Fehler „cloudflare kann nur innerhalb der Organisation verwendet werden" = der **OAuth-Consent-Screen**
|
||||||
|
des Google-Projekts steht auf **User Type = Internal** (nur Workspace-Org-Konten). Gelis Gmail ist extern
|
||||||
|
→ blockiert. Fix (der CF-Google-IdP `c757…` bleibt unverändert, nur die Consent-Einstellung ändert sich):
|
||||||
|
1. <https://console.cloud.google.com> → richtiges Projekt (das, aus dem die Google-OAuth-Client-ID stammt).
|
||||||
|
2. **APIs & Services → OAuth consent screen** (neu: „Google Auth Platform → Audience").
|
||||||
|
3. **User Type / Audience von *Internal* auf *External*** stellen.
|
||||||
|
4. **Veröffentlichen:** Publishing status → **„In production"** (dann kann jedes Google-Konto rein).
|
||||||
|
Alternativ in „Testing" bleiben und **Gelis Gmail als Test user** eintragen.
|
||||||
|
5. Danach Login-Test mit Gelis Google-Konto. (Bei „External/unverified" kommt ggf. ein
|
||||||
|
„unbestätigte App"-Hinweis — für den kleinen Kreis unkritisch, oder App verifizieren lassen.)
|
||||||
|
|
||||||
|
## §B — Meta/Facebook als Login (User legt App an, dann CF-IdP durch mich)
|
||||||
|
Es gibt **noch keine** Facebook-App (Creds leer). Schritte:
|
||||||
|
1. <https://developers.facebook.com> → **My Apps → Create App** → Use case **„Authenticate and request
|
||||||
|
data from users with Facebook Login"** (Consumer).
|
||||||
|
2. Produkt **Facebook Login** hinzufügen.
|
||||||
|
3. **Facebook Login → Settings → Valid OAuth Redirect URIs** =
|
||||||
|
`https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/callback`
|
||||||
|
4. **App settings → Basic:** **App ID** + **App Secret** → an mich (in `~/.secrets`, **nicht** in den Chat),
|
||||||
|
ich trage sie in `mcp/cloudflare/.env` (`FACEBOOK_APP_ID/SECRET`) ein und lege den CF-Facebook-IdP an.
|
||||||
|
5. **App Mode → Live** (sonst dürfen nur App-Rollen/Tester rein); Geli ggf. als Tester eintragen.
|
||||||
|
6. ⚠️ CF matcht die Policy über die **E-Mail**, die Facebook zurückgibt — diese E-Mail muss in
|
||||||
|
`zt-humans` stehen (s. §C).
|
||||||
|
|
||||||
|
## §C — Geli: Zugangsmethoden (Matrix)
|
||||||
|
CF Access matcht Personen über die **E-Mail** der Identität (nicht über Usernamen). Stand:
|
||||||
|
| Methode | Gelis Angabe | CF-Status | To-Do |
|
||||||
|
|---|---|---|---|
|
||||||
|
| OTP (E-Mail-Code) | `angelikahy@outlook.de` | ✅ in `zt-humans` | — |
|
||||||
|
| OTP + Google | `pillepalle670@gmail.com` | ✅ **heute ergänzt** | Google-Fix §A nötig, damit Google-Login geht |
|
||||||
|
| Facebook | User `35Lika.Ange` | ⏳ | Facebook-App (§B); dann die **E-Mail** ihres FB-Kontos in `zt-humans` — welche ist das? (vermutlich eine der beiden obigen) |
|
||||||
|
| Instagram | `angelikahy` | ❌ **CF Access hat keinen Instagram-IdP** | Nicht möglich als Direkt-Login → Geli nutzt Facebook/Google/OTP |
|
||||||
|
|
||||||
|
→ Sobald Gelis FB-Konto-E-Mail bekannt ist und sie eine der schon hinterlegten Adressen ist, ist Facebook
|
||||||
|
automatisch abgedeckt. Sonst diese E-Mail zusätzlich in `zt-humans` aufnehmen.
|
||||||
|
|
||||||
|
## §D — App-Launcher `emsenhuber.cloudflareaccess.com` überarbeiten
|
||||||
|
**Ist-Analyse (live):** Der Launcher (App `6df5c431`, `landing_page_design={}` = ungestylt) zeigt Kacheln
|
||||||
|
aus den Access-Apps mit `app_launcher_visible=true`:
|
||||||
|
- `1dbc45ca` **„hass, vw, gitea"** (self_hosted, Domain `vw.emsenhuber.net`) → **eine** Kachel, verlinkt nur vw.
|
||||||
|
- `16aaec78` **`*.emsenhuber.net`** (Wildcard) → Kachel ohne sinnvolles Ziel.
|
||||||
|
- `e6a7991f` **Terpenfinder v3** (`420.teilzeitladen.xyz`) → eigene Kachel ✅.
|
||||||
|
Deshalb „stimmt nicht mehr": vw/hass/gitea teilen sich eine Kachel, die Wildcard-Kachel stört.
|
||||||
|
|
||||||
|
**Ziel:** je ein sauberer Button für **Vaultwarden, Home Assistant, Terpenfinder** + freundlichere Seite.
|
||||||
|
**Zwei Wege (Entscheidung offen):**
|
||||||
|
- **D1 — CF-nativ (empfohlen, „diese Seite"):** je ein **Bookmark-App** (type `bookmark`) für vw/hass/
|
||||||
|
terpenfinder mit Name + Logo (eigene Kachel/Link), die Wildcard-Kachel aus dem Launcher nehmen,
|
||||||
|
`landing_page_design` setzen (Titel z.B. „BF-Zugänge", Begrüßung, Farbe, Logo). Reversibel, in Minuten.
|
||||||
|
Grenze: CF-Styling = Titel/Text/Farbe/Logo + uniforme Kacheln.
|
||||||
|
- **D2 — Custom-Landing-Page:** eigene HTML-Seite (Caddy, hinter Access) mit voller Gestaltung/großen
|
||||||
|
Buttons. Mehr Design, mehr Aufwand (eigener vhost + Access-Regel).
|
||||||
|
|
||||||
|
## §E — dw: `420.teilzeitladen.xyz` lokal auflösen (FritzBox `10.95.10.1`)
|
||||||
|
**Ehrliche FritzBox-Antwort:** Eine FritzBox kann **keinen** freien „Hostname → beliebige IP"-Override im
|
||||||
|
GUI (nur *DNS-Rebind-Schutz-Ausnahmen* = Whitelist gegen Rebind-Blockade, **ohne** IP-Zuweisung). Ein
|
||||||
|
sauberer Split-Horizon für genau diesen Namen braucht daher einen **kleinen lokalen Resolver** am dw
|
||||||
|
(dnsmasq/AdGuard/Pi-hole auf einem Dauerläufer): `address=/420.teilzeitladen.xyz/<BF-Caddy-über-WG>` +
|
||||||
|
Rest weiterleiten; diesen Resolver als DNS der FritzBox bzw. der Clients setzen.
|
||||||
|
⚠️ **Vor Umsetzung verifizieren** (nicht aus dem Kopf): FritzOS-Version am dw + ob ein Dauerläufer da ist.
|
||||||
|
**BF-seitige Prereqs (fehlen noch):**
|
||||||
|
1. **Caddy-vhost** `420.teilzeitladen.xyz` → macmini-Worker (`10.40.20.167:8890`) **mit Cert**
|
||||||
|
(DNS-01, `…4148` hat jetzt teilzeitladen.xyz-DNS).
|
||||||
|
2. **WG-Route** dw → `10.40.20.40` (Caddy) muss stehen.
|
||||||
|
3. **Split-Horizon** für `420.teilzeitladen.xyz` (heute liefern auch `.36/.37` **public** für diesen Namen).
|
||||||
|
Cross-Projekt (teilzeitladen.xyz). Alternative aus Ur-Handoff: Gelis Gerät in **WARP** (Session statt Login,
|
||||||
|
CGNAT unkritisch) + Access-Policy auf die WARP-Identität — spart den ganzen dw-DNS-Umbau.
|
||||||
|
|
||||||
|
## Entscheidungen (2026-09-01, getroffen)
|
||||||
|
1. **App-Launcher:** ✅ **D1** (CF-nativ) — umgesetzt (s. §D / Punkt 5).
|
||||||
|
2. **Gelis Facebook-Mail:** ✅ `angelikahy@outlook.de` — schon in `zt-humans`, Facebook damit abgedeckt.
|
||||||
|
3. **dw-Ansatz:** ✅ Geli per **WARP** (sofort); Site-DNS-Uplink als **TP43** ausgelagert.
|
||||||
|
|
||||||
|
## Verbleibende offene To-Dos (User)
|
||||||
|
- ~~**§A Google-Consent-Screen** auf *External*~~ ✅ **erledigt** — Google-Login läuft (2026-09-01).
|
||||||
|
- ~~**§B Facebook-App anlegen**~~ ❌ **abgesagt für TP41** (User-Entscheid 2026-09-01) — optional; §B
|
||||||
|
bleibt als Anleitung stehen, falls später doch gewünscht.
|
||||||
|
- ~~**d47435-Revoke**~~ ✅ erledigt · ~~**T3-WARP-Client-Verify**~~ ✅ ok (User, 2026-09-01).
|
||||||
|
- **Geli in WARP:** ✅ **CF-seitig nichts zu tun** — Enrollment läuft über App „Warp Login App"
|
||||||
|
(`6c32fe23…`) → `bf-users` → Gruppe `zt-humans`, die Gelis Mails **schon enthält**; sie darf bereits
|
||||||
|
enrollen. Terpenfinder-`geli`-Policy lässt sie per E-Mail durch. **Rest = Handgriff auf Gelis Gerät:**
|
||||||
|
WARP installieren, Team `emsenhuber`, Login OTP `angelikahy@outlook.de` oder Google `pillepalle670@gmail.com`.
|
||||||
|
*Permission-Frage:* CF-seitig keine nötig; `/access/seats` per MCP nicht nutzbar (`10405`, Endpoint will
|
||||||
|
User-Token statt Account-Token — kein Scope-Fix, für Enroll irrelevant).
|
||||||
|
- **T5 (TP41)** ✅ GitHub- **und** Google-Login laufen (bestätigt).
|
||||||
|
- **TP43** (dw-Site-DNS) bei Gelegenheit.
|
||||||
|
|
||||||
|
> **TP41-Auth-Kern damit abgeschlossen (2026-09-01):** T2/T3/T4/T5 ✅, FB abgesagt, d47435-Revoke ✅,
|
||||||
|
> WARP-Verify ✅, Geli CF-seitig fertig. **Nichts mehr auf Claude-/CF-Seite offen** — verbleibt nur
|
||||||
|
> Gelis WARP-Client auf ihrem Gerät.
|
||||||
166
docs/handoff-20260901-tp41.md
Normal file
166
docs/handoff-20260901-tp41.md
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
# Handoff 2026-09-01 — TP41 CF-Access-Reparatur + Zero-Trust-Konsolidierung
|
||||||
|
|
||||||
|
> **Branch `tp41-cf-access-repair`.** Alle CF-Änderungen sind **live via API** gemacht (Schreibpfad
|
||||||
|
> `CF_API_TOKEN` + curl, cf-tunnel-mapping.md §11). Working-Tree: `docs/cf-backup/tp41-20260901/`
|
||||||
|
> (Snapshots, secret-frei) + dieser Handoff + `teilprojekte.md`-Eintrag; `mcp/cloudflare/.env`
|
||||||
|
> (gitignored) wurde geändert (Token-Konsolidierung). Helper-Skripte lagen im Scratchpad/`/tmp`
|
||||||
|
> (`cf_session.py`, `cf_harden.py`, `cf_groups.py`, `cf_vwadmin.py`, `cf_carveout.py`, `t4a.py`).
|
||||||
|
|
||||||
|
## Auslöser
|
||||||
|
Zugriff auf CF-geschützte Apps zwang Christoph **und Geli** faktisch **täglich** zum E-Mail-OTP-Login,
|
||||||
|
obwohl 30 Tage gelten sollten; föderierte Logins (GitHub/Google) „funktionieren nicht". Root-Causes:
|
||||||
|
(1) **Team-Domain-Rename `bf4` → `emsenhuber`** ⇒ Provider-OAuth-Apps kennen die neue Callback-URL nicht;
|
||||||
|
(2) App-**Session 24h**; (3) diverse Ordnungs-/Sicherheitslecks. Daraus wurde eine
|
||||||
|
Zero-Trust-Konsolidierungs-Runde.
|
||||||
|
|
||||||
|
## ✅ LIVE erledigt (verifiziert)
|
||||||
|
- **Session 720h** auf Wildcard (`16aaec78`), vw-App (`1dbc45ca`), Warp-Login (`6c32fe23`).
|
||||||
|
- **login_method-Loch geschlossen:** reusable Policy **`bf-users` (`4ccc9b7e`)** = `allow` mit **nur**
|
||||||
|
`email:christoph@emsenhuber.com` + `email:<geli>@outlook.de` (kein `login_method`-Include mehr).
|
||||||
|
Attached an Wildcard / App-Launcher (`6df5c431`) / Warp-Login. Reusable Policy `8e44f5d1`
|
||||||
|
**entkoppelt** → hängt jetzt **nur noch an Terpenfinder** (Fremdprojekt, nicht angefasst).
|
||||||
|
- **Access-Gruppen (Prefix `zt-`):** `zt-humans` (`6eeec7f3`, christoph+geli), `zt-home`
|
||||||
|
(`c0f8ba0d`, IP `90.146.33.177/32`), `zt-automation` (`1b60b608`, svc-token `65f1821a`).
|
||||||
|
Policies `bf-users` / `bf4` (`5165d139`) / `claude-mcp` (`39fe0e9d`) zeigen jetzt **auf die Gruppen**
|
||||||
|
→ Personen-/IP-/Token-Pflege künftig nur noch in der Gruppe.
|
||||||
|
- **vw = voll Bypass** (Passwort-Lebensader: Web-Vault + Bitwarden-App/Extension/Desktop offen, **kein**
|
||||||
|
CF-Login davor). Ein Carve-out-Versuch (nur Client-Pfade bypass, Rest gaten) wurde **verworfen**:
|
||||||
|
CF-**Bypass kurzschließt** spezifischere path-Apps, und `/` (Web-Vault-Root) teilt den Prefix mit
|
||||||
|
`/admin` — nicht trennbar. Die 3 Experiment-Apps wurden wieder gelöscht.
|
||||||
|
- **`vw.emsenhuber.net/admin` öffentlich geblockt** per **WAF-Custom-Rule** (Zone-Ruleset
|
||||||
|
`416af72d0dd54853805875bf5752afd7`, Regel `f26744eb…`, `action=block`,
|
||||||
|
expr `http.host eq "vw.emsenhuber.net" and (path eq "/admin" or starts_with(path,"/admin/"))`).
|
||||||
|
**Verifiziert über CF-Edge** und vom User per **Mobilfunk** („you have been blocked"). Lokal (Heim-LAN,
|
||||||
|
Split-Horizon → Caddy) und später WARP-intern bleibt `/admin` erreichbar.
|
||||||
|
- **API-Token konsolidiert:** `mcp/cloudflare/.env` `CF_API_TOKEN` = **`…4148`** (id `493ed8bd…`),
|
||||||
|
Backup `mcp/cloudflare/.env.bak-tp41-20260901`. Der User hat `…4148` anschließend **vollständig**
|
||||||
|
gemacht (u.a. **Access: Service Tokens Write** + **Zero Trust Write** + WAF + Groups + DNS für
|
||||||
|
emsenhuber.net **und** teilzeitladen.xyz). **Altes Token `…2174` (id `9984078812bc18d4ebb6a64b570144f2`)
|
||||||
|
ist am 2026-09-01 vom User revoked ✅.**
|
||||||
|
|
||||||
|
> ⚠️ **API-Token-Altlast → ARBEITSPAKET für die Folgesession (User 2026-09-01):** der User fand ein
|
||||||
|
> weiteres API-Token **`b00722522c685920f73cfcc62e9555d9`** mit **exakt denselben Permissions wie
|
||||||
|
> `d47435d5c86b764bbf7c5b19ae827033`** — vermutlich eine **blaueblüten/Terpene-Altlast**.
|
||||||
|
> **Auftrag: Projekte nachziehen** — feststellen, welcher Consumer welches der beiden Tokens nutzt,
|
||||||
|
> auf **eines** konsolidieren, das verwaiste dann abbauen. Token-/Credentials-Hygiene (TP30-nah).
|
||||||
|
|
||||||
|
## 🔜 OFFEN (Token reicht jetzt für alles außer T5)
|
||||||
|
- **T4 — kumulatives Service-Token `zt-svc`** (Basis: `t4a.py`, war nur an fehlender Perm gescheitert):
|
||||||
|
1. `claude-mcp` (`65f1821a`) **rename → `zt-svc`** + **rotate** (Secret einmalig).
|
||||||
|
2. `terpene-svc`-Policy (`bf7d2e5e`) **additiv** += group `zt-automation` (akzeptiert dann alt **und** neu).
|
||||||
|
3. Secret ablegen: `mcp/cloudflare/.env` (`CF_ACCESS_CLIENT_ID/SECRET`) **und**
|
||||||
|
`~/.secrets/credentials.env` — dort auch die Terpene-Aliase **`TERPENE_ACCESS_CLIENT_ID/SECRET`**
|
||||||
|
(die die Tools lesen) **und** `TERPENE_VERIFY_CF_CLIENT_ID/SECRET` (beide zeigten auf terpene-verify-v2).
|
||||||
|
4. **Verify Terpene:** `python3 ~/dev/blaueblueten-n8n/tools/test_whoami.py`
|
||||||
|
(→ `420.teilzeitladen.xyz/api/admin/whoami`, muss weiter funktionieren).
|
||||||
|
5. Erst **nach** grünem Verify: `terpene-verify-v2` aus `terpene-svc` entfernen, dann
|
||||||
|
**`terpene-verify` (`a6be7efa`) + `terpene-verify-v2` (`1082bad1`) löschen**.
|
||||||
|
6. `~/.secrets/README.md`-Index nachziehen. opencode/`exports.zsh` optional (gui-env-sync).
|
||||||
|
→ End: **ein** Service-Token `zt-svc` über Gruppe `zt-automation` in allen Apps + Consumern.
|
||||||
|
- **T3 — WARP löst `emsenhuber.net` intern auf** (User-Wahl „Variante 2"): WARP-Device-Profile
|
||||||
|
`fallback_domains` += `emsenhuber.net` → interner Resolver **`10.40.20.1`** (liefert Split-Horizon
|
||||||
|
`*.emsenhuber.net` → Caddy `10.40.20.40`). Route **`10.40.0.0/16` existiert** (tunnel `d1da7c8a`).
|
||||||
|
Danach `/admin` + alle internen Namen über **WARP** via Caddy erreichbar (kein CF/WAF). ⚠️ **account-/
|
||||||
|
profilweit** → als Lifecycle-Change **proaktiv melden**. (Token hat jetzt Zero-Trust-Write.)
|
||||||
|
- **T2 — 404 statt 403 (User-Wunsch):** WAF-**Custom-Response ist Free-Plan-gesperrt** („not entitled to
|
||||||
|
use a custom response"). 403-Block bleibt — außer man macht es **Origin-seitig in Caddy** (`respond 404`
|
||||||
|
wenn Header `CF-Connecting-IP` gesetzt = via CF gekommen; lokal/WARP-intern ohne Header → durchlassen).
|
||||||
|
Das komponiert sauber mit T3 (WARP-intern trifft Caddy ohne CF-Header). Eigener Caddy-Deploy (LXC 130).
|
||||||
|
- **T5 — IdP-Konsolen (USER-Hausaufgabe, DER Daily-OTP-Fix, bestätigt kaputt):** User-Test lieferte
|
||||||
|
GitHub **„The redirect_uri is not associated with this application"**. Fix in den **Provider-Konsolen**:
|
||||||
|
- **GitHub** OAuth-App `Ov23li2h96hAkGRPJXYV` → *Authorization callback URL*
|
||||||
|
- **Google** OAuth-Client → *Authorized redirect URIs*
|
||||||
|
- beide = **`https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/callback`**.
|
||||||
|
Danach Login-Test je Provider; Gegenprobe CF-Audit `GET /accounts/…/access/logs/access_requests`
|
||||||
|
(`identity_provider=github/google` statt `onetimepin`).
|
||||||
|
- **T6 (optional) — Rate-Limit:** vw `/identity/connect/token` + gitea-Login. **Kein** Managed-Challenge
|
||||||
|
vor vw-API/`/identity`, git-/api-Pfaden, `assetlinks.json` (bricht Bitwarden-Clients / git / MCP).
|
||||||
|
- **T7 — Doku:** ggf. `docs/tp41-cf-access-repair-runbook.md` (Detail-Runbook) ergänzen.
|
||||||
|
|
||||||
|
## 🆕 NEU — dw/Geli an der Auth vorbei (Terpenfinder, CGNAT)
|
||||||
|
Ziel: dw erreicht `420.teilzeitladen.xyz` **ohne** CF-Login. IP-Bypass wie `bf4` scheitert an Gelis
|
||||||
|
**CGNAT**. **Eleganteste Lösung (User-Idee): Split-Horizon am dw-Standort** — dw-FritzBox setzt lokalen
|
||||||
|
DNS-Override `420.teilzeitladen.xyz` → BF-Caddy (`caddy-bf1` / `10.40.20.40`), erreichbar über den
|
||||||
|
bestehenden **WG-Tunnel dw↔core**; Caddy braucht einen Block `420.teilzeitladen.xyz` → macmini-Worker
|
||||||
|
(`10.40.20.167:8890`) **mit Cert** (DNS-01; Token hat jetzt DNS-Write für `teilzeitladen.xyz`).
|
||||||
|
Vorab prüfen: (a) FritzBox-Host-Override möglich; (b) WG-Route dw→10.40.20.40; (c) Worker wirklich auf
|
||||||
|
`macmini:8890` von Caddy erreichbar. **Cross-Projekt** (teilzeitladen.xyz). Alternative: Geli-Gerät in
|
||||||
|
**WARP** (Session statt Login, CGNAT unkritisch) + Access-Policy auf WARP-Identität.
|
||||||
|
|
||||||
|
## ❓ Scope-Klärung für die neue Session
|
||||||
|
Der User nannte „TP3/TP4/TP5 gleich mitnehmen" — **diese sind laut `teilprojekte.md` abgeschlossen**
|
||||||
|
(TP3 WLAN-Rebuild ✅, TP4 ap1-Router/WAN-Edge ✅, TP5 deny-by-default ✅). Vermutlich Zahlendreher.
|
||||||
|
**Offene Kandidaten:** TP6 (CAKE, Projektziel), TP16.1 (WARP-Mesh nativ), TP16.3, TP24 (DC-USV),
|
||||||
|
TP25 (WLAN-Signal/Re-Walk). **Vor Start klären, welche gemeint sind** (je eigene Session empfohlen;
|
||||||
|
Subagenten nur für parallele, abgegrenzte Teilaufgaben innerhalb einer Session).
|
||||||
|
|
||||||
|
## 🧭 Test-Vantage-Lehre (wichtig, hat hier Stunden gekostet)
|
||||||
|
Von der **Heim-IP** löst `*.emsenhuber.net` → `10.40.20.40` (Caddy) und **umgeht CF komplett** — CF-Access/
|
||||||
|
WAF-Gating ist von zuhause **nicht** sichtbar (alles wirkt „offen"). Edge-Verhalten **erzwingen** mit
|
||||||
|
`curl --resolve <host>:443:<CF-anycast-IP>` (CF-IP via `dig @1.1.1.1 +short <host>`). **WebFetch ist für
|
||||||
|
Access/Redirects unzuverlässig** (erreichte Origin trotz Gating) — **nicht** als Beweis verwenden; echter
|
||||||
|
Remote-Test = User-Gerät im Mobilfunk.
|
||||||
|
|
||||||
|
## Referenz-IDs
|
||||||
|
Apps: Wildcard `16aaec78` · vw/hass/gitea-Bundle `1dbc45ca` · App-Launcher `6df5c431` · Warp-Login
|
||||||
|
`6c32fe23` · Terpenfinder v3 `e6a7991f`. Policies: `bf-users` `4ccc9b7e` · `bf4` `5165d139` · `claude-mcp`
|
||||||
|
`39fe0e9d` · `terpene-svc` `bf7d2e5e` · (Terpene-only) `8e44f5d1`. Gruppen: `zt-humans` `6eeec7f3` ·
|
||||||
|
`zt-home` `c0f8ba0d` · `zt-automation` `1b60b608`. Service-Tokens: `claude-mcp`→`zt-svc` `65f1821a` ·
|
||||||
|
`terpene-verify` `a6be7efa` (Waise) · `terpene-verify-v2` `1082bad1`. WAF: Ruleset `416af72d…`, Regel
|
||||||
|
`f26744eb…`. Team-Domain `emsenhuber.cloudflareaccess.com` (Org-`name`-Altlabel `bf4…` nicht anfassen).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Update — Folgesession 2026-09-01 (T4 durch, Token-Altlast geklärt, Drift-Korrekturen)
|
||||||
|
|
||||||
|
### T4 — zt-svc ✅ DURCH (Variante B, inkl. Worker)
|
||||||
|
- `claude-mcp` (`65f1821a`) → **`zt-svc`** umbenannt **und rotiert** (Secret in
|
||||||
|
`mcp/cloudflare/.env` `CF_ACCESS_CLIENT_ID/SECRET` + `~/.secrets/credentials.env`; dort
|
||||||
|
`TERPENE_ACCESS_*` und `TERPENE_VERIFY_CF_*` jetzt = zt-svc). client_id
|
||||||
|
`ad1b47dee8ba9625eebb30247d163dda.access`.
|
||||||
|
- **Worker-Pin gefunden** (`blaueblueten-n8n/worker/src/index.js:44`): Admin wird nur dem Token mit
|
||||||
|
`common_name == env.TERPENE_SVC_CLIENT_ID` gewährt. Deshalb Variante B: **`wrangler secret put
|
||||||
|
TERPENE_SVC_CLIENT_ID`** = zt-svc-client_id (wrangler-OAuth-Session, `workers_scripts:write` — die
|
||||||
|
„Deploy blockiert"-Notiz galt nur für API-Tokens). Kein Code-Redeploy nötig.
|
||||||
|
- `terpene-svc` (`bf7d2e5e`) include = **nur noch Gruppe `zt-automation`** (`1b60b608`).
|
||||||
|
**`terpene-verify` (`a6be7efa`) + `terpene-verify-v2` (`1082bad1`) gelöscht.** Einziges verbleibendes
|
||||||
|
Access-Service-Token = **`zt-svc`**. Verify: `test_whoami.py` → `admin:true`, common_name = zt-svc. ✅
|
||||||
|
|
||||||
|
### Token-Altlast (b00722 / d47435) — GEKLÄRT (Rollen waren im Ur-Handoff vertauscht)
|
||||||
|
- **`b00722`** ist **NICHT** die Altlast, sondern das **LIVE** von Caddy genutzte DNS-01-Token
|
||||||
|
(LXC130 `/etc/caddy/caddy.env`, SSoT). Certs valide bis Nov 2026.
|
||||||
|
- **`d47435`** = **stale Klartext-Kopie** in `credentials.env` (gleiche Perms, kein Live-Consumer).
|
||||||
|
n8n hält **0 Credentials**, blaueblüten hält keinen der beiden. `credentials.env`-Kopie am
|
||||||
|
2026-09-01 auf **b00722** realignt (SSoT-Abgleich).
|
||||||
|
- 🔜 **USER-Aktion:** **`d47435` revoken** (Dashboard oder `…4148` kurz „Account API Tokens Write"
|
||||||
|
geben — `…4148` kann API-Tokens weder listen noch revoken, Bestätigung 9109/1000).
|
||||||
|
|
||||||
|
### Drift-Korrekturen (live gemessen)
|
||||||
|
- **T3-Resolver:** interner Split-Horizon liegt auf **`10.40.20.36`/`.37`** (→ `*.emsenhuber.net = .40`),
|
||||||
|
**nicht** auf `10.40.20.1` (der liefert public CF). T3-fallback_domains müssen auf `.36/.37` zeigen.
|
||||||
|
- **`10.40.20.16/.17`** (AdGuard laut CLAUDE.md) sind **stale** → neu als **TP42** angelegt
|
||||||
|
(`docs/handoff-20260901-tp42-naming.md`): `.bf.lan`/`.bf.emsenhuber.net`-Vereinheitlichung.
|
||||||
|
|
||||||
|
### 🔴 Leak (Transparenz)
|
||||||
|
Beim maskierten Lesen von `credentials.env` ist der Wert **`TERPENE_ANDROID_KEYSTORE_PW`** ins
|
||||||
|
Sitzungstranskript geraten (Regex-Loch `_PW`). **Rotation erwägen** (Terpene-Android-Signing).
|
||||||
|
|
||||||
|
### T3 — WARP-intern ✅ APPLIED (2026-09-01)
|
||||||
|
- Ziel-Profil geklärt: das custom „Cloudflare Mesh"-Profil matcht **nur** `warp_connector@…` → Christoph/Geli
|
||||||
|
laufen im **Default-Profil** (`0197d1b3`). `fallback_domains += emsenhuber.net → [10.40.20.36, 10.40.20.37]`
|
||||||
|
dort gesetzt (15 Suffixe). Tunnel-Route `10.40.0.0/16` via `d1da7c8a` existiert; Exclude-Liste enthält
|
||||||
|
**kein** `10.0.0.0/8` (nur `192.168/16` + CGNAT-minus-Mesh) → `.36/.37` sind über WARP routbar.
|
||||||
|
- ⏳ **User-Verify:** WARP am Gerät **neu verbinden** (Profil-Cache), dann `dig vw.emsenhuber.net → .40`
|
||||||
|
und `/admin` über WARP erreichbar.
|
||||||
|
|
||||||
|
### T2 — 404 statt 403 ✅ DURCH (2026-09-01)
|
||||||
|
- Caddyfile `vw`-Block: `@cf_admin {header CF-Connecting-IP *; path /admin /admin/*} → respond 404`.
|
||||||
|
Deploy LXC130 (Backup `/etc/caddy/Caddyfile.bak-tp41-20260901`), reload ok.
|
||||||
|
- WAF-Regel **`f26744eb` gelöscht** (nur noch die gitea-skip-Regel `3c707c3c` im Ruleset).
|
||||||
|
- Verify: **Edge `/admin` = 404** (war 403), Edge `/` = 200, **lokal/WARP `/admin` = 200**. ✅
|
||||||
|
|
||||||
|
### Noch offen
|
||||||
|
- **d47435-Revoke** (User, Account API Tokens Write). **T3-Client-Verify** (User, WARP neu verbinden).
|
||||||
|
- **T5** (GitHub/Google-Callbacks — der eigentliche OTP-Fix, User) — **Schritt-für-Schritt:**
|
||||||
|
[`docs/tp41-t5-idp-callback-anleitung.md`](tp41-t5-idp-callback-anleitung.md). **T6/T7/dw-Geli**.
|
||||||
|
- 🔴 **`TERPENE_ANDROID_KEYSTORE_PW`-Rotation erwägen** (Transkript-Leak, s.o.).
|
||||||
40
docs/handoff-20260901-tp42-naming.md
Normal file
40
docs/handoff-20260901-tp42-naming.md
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# Handoff 2026-09-01 — TP42 Namens-Vereinheitlichung bf.lan → emsenhuber.net
|
||||||
|
|
||||||
|
> **Angelegt aus einem TP41-Randbefund.** Reine Anlage + Bestandsaufnahme; **keine Live-Änderung**
|
||||||
|
> in dieser Session. Umsetzung = eigene Session.
|
||||||
|
|
||||||
|
## Auslöser
|
||||||
|
Beim Verifizieren des T3-Resolvers (TP41) live gemessen:
|
||||||
|
|
||||||
|
| Resolver | vw.emsenhuber.net | Bewertung |
|
||||||
|
|---|---|---|
|
||||||
|
| 10.40.20.36 / .37 | 10.40.20.40 | ✅ korrekt (reale Client-Resolver, via DHCP) |
|
||||||
|
| 10.40.20.1 (Core) | 188.114.96.10 / .97.10 | public CF (Core macht DoH→CF) |
|
||||||
|
| 10.40.20.16 | CNAME nginx-bf.emsenhuber.net (kein A) | 🔴 stale |
|
||||||
|
| 10.40.20.17 | nginx.bf.emsenhuber.net + 10.40.41.123 | 🔴 stale, totes VLAN1 |
|
||||||
|
|
||||||
|
Daneben: Core `/ip dns static` = 11× `*.bf.lan`; Caddyfile doppelt jeden internen Namen als
|
||||||
|
`*.bf.emsenhuber.net`.
|
||||||
|
|
||||||
|
## Befund-Fundstellen (Start für die Umsetzung)
|
||||||
|
- **Core (`crs326-core`) `/ip dns static`:** router.lan, core.bf.lan, macmini.bf.lan, pve.bf.lan,
|
||||||
|
pve-mcp.bf.lan, nas.bf.lan, docker.bf.lan, platform-pg.bf.lan, ap1.bf.lan, ap2.bf.lan, branch.bf.lan.
|
||||||
|
- **Caddyfile (`caddy/Caddyfile`, LXC130/.40):** `*.bf.emsenhuber.net`-Aliase an nautobot/ccu/iobroker/
|
||||||
|
z2m25/pve-mcp/ha-mcp/n8n-mcp/gitea-mcp/pve/portainer/pbs.
|
||||||
|
- **AdGuard `.36`/`.37`:** aktive Split-Horizon-Rewrites `*.emsenhuber.net → .40` (Sollquelle).
|
||||||
|
- **Stale `.16`/`.17`:** alte AdGuard, `nginx*`-Antworten + `10.40.41.123` → abbauen/klären.
|
||||||
|
- **DHCP:** Search-/Domain-Option je Netz prüfen (welcher Suffix wird verteilt?).
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
Ein interner Namensraum `emsenhuber.net` (Split-Horizon `.36/.37 → .40`). `.bf.lan` +
|
||||||
|
`.bf.emsenhuber.net` raus. Sollzustand in Nautobot (TP37).
|
||||||
|
|
||||||
|
## Offen / Entscheidungen für die Umsetzungssession
|
||||||
|
1. Zielschema fix: nur `emsenhuber.net`, oder `bf.emsenhuber.net` als **einzige** interne Zone?
|
||||||
|
2. Migrationsreihenfolge ohne DNS-Loch (erst neue Namen, dann alte weg).
|
||||||
|
3. `.16/.17`: laufen die noch, gehören sie abgebaut? (mit TP für AdGuard-Konsolidierung abgleichen)
|
||||||
|
4. Nautobot-Erfassung: Custom Field / Records — abhängig von TP37-Datenmodell.
|
||||||
|
|
||||||
|
## Nicht anfassen
|
||||||
|
Öffentliche Zone `emsenhuber.net` (CF); Resolver-Zuordnung `.36/.37`; `.1`-DNS-Reste auf Clients
|
||||||
|
(User: „bis jetzt kein Problem"). Test-Vantage-Lehre aus TP41 beachten (von zuhause umgeht man CF).
|
||||||
72
docs/handoff-20260902-cf-estate-nautobot-ssot.md
Normal file
72
docs/handoff-20260902-cf-estate-nautobot-ssot.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Handoff 2026-09-02 — CF-Estate als SSoT in Nautobot (TP44, Etappe 1)
|
||||||
|
|
||||||
|
> Scope: die Cloudflare-Infrastruktur (Tunnel + App-Ebene) als strukturierte, abfragbare SSoT in
|
||||||
|
> Nautobot abbilden. Etappe 1 = **manuelle Erstbefüllung** über die nativen `nautobot.cloud`-Modelle.
|
||||||
|
> Ausgelöst aus der DB-Pfad-Session (siehe `cf-tunnel-mapping.md` §17).
|
||||||
|
|
||||||
|
## Entscheidung (User, 2026-09-02)
|
||||||
|
|
||||||
|
- **SSoT-Modus:** Etappen — *manuell jetzt, Sync später*. `cf-tunnel-mapping.md` wird zum Snapshot/Zeiger.
|
||||||
|
- **Scope Etappe 1:** *volle CF-Estate* (Tunnel + WARP-Connector + VPC-Service + Hyperdrive + Workers +
|
||||||
|
alle Access-Apps).
|
||||||
|
- **Grundsatz:** Nautobot kann CF **nicht steuern** → „SSoT" = **Dokumentations**-SSoT. CF bleibt
|
||||||
|
operativer Master.
|
||||||
|
|
||||||
|
## Warum ein eigenes TP (TP44), nicht TP36
|
||||||
|
|
||||||
|
TP36 ist in `teilprojekte.md` eng auf *`teilprojekte.md`-Live-Daten + Datei-Aufteilung* geschnitten.
|
||||||
|
Dieselbe Idee (Nautobot als Doku-SSoT), aber andere Dimension → **TP44** (nächste freie Nummer nach TP43).
|
||||||
|
Quick-Index-Zeile + TP-Abschnitt in `teilprojekte.md` sind **noch offen** (die Datei trug bei dieser
|
||||||
|
Session fremde, ungestagte Änderungen — bewusst nicht mit angefasst, um den Commit sauber zu halten).
|
||||||
|
|
||||||
|
## Modell (Nautobot 3.1.7, App `nautobot.cloud`)
|
||||||
|
|
||||||
|
```
|
||||||
|
Manufacturer "Cloudflare"
|
||||||
|
└─ CloudAccount "Cloudflare" (account_number = 393c051f3accf51881d6c98fc5b66c36)
|
||||||
|
├─ CloudResourceType ×6
|
||||||
|
│ cloudflared Tunnel · WARP Connector → cloud.cloudnetwork
|
||||||
|
│ VPC Service · Hyperdrive · Worker · Access Application → cloud.cloudservice
|
||||||
|
├─ CloudNetwork ×4 bf-public · fm.emsenhuber.net · terpene-db · bf-core-mesh
|
||||||
|
└─ CloudService ×14 VPC terpene-db · Hyperdrive terpene(ro)+terpene-rw ·
|
||||||
|
Worker terpene+blaueblueten-n8n · 9× Access-App
|
||||||
|
```
|
||||||
|
|
||||||
|
- **IDs/Ports/Hostnames/Origins** liegen je Objekt in `extra_config` (JSON).
|
||||||
|
- **Service↔Tunnel-Verknüpfung** über das Through-Modell `cloud-service-network-assignments` (3 Rows):
|
||||||
|
`terpene-db↔terpene-db`, `*.emsenhuber.net↔bf-public`, `hass, vw, gitea↔bf-public`.
|
||||||
|
- **Prefix-Routen** (`10.40.0.0/16` bf-core-mesh, `10.101.0.0/24` fm) sind **nicht** als
|
||||||
|
`cloud-network-prefix-assignments` gesetzt, weil die Prefixe nicht in IPAM existieren → in
|
||||||
|
`extra_config.route` belassen (best-effort im Seeder, greift automatisch, sobald die Prefixe in IPAM sind).
|
||||||
|
|
||||||
|
## Werkzeug
|
||||||
|
|
||||||
|
`nautobot/tp44-cf-estate-seed.py` — **idempotenter** Seeder (get-or-create), Datenstand als Inline-Struktur.
|
||||||
|
Token aus `docs/nas-vlan20/stacks/210-nautobot.yml` (gitignored). Aufruf: Dry-Run default,
|
||||||
|
`--commit` schreibt. Re-Run = 26/26 „existiert".
|
||||||
|
*(Datei am 2026-09-06 von `tp36-…` auf `tp44-cf-estate-seed.py` umbenannt — Präfix jetzt korrekt.)*
|
||||||
|
|
||||||
|
## Gotchas (für Etappe 2 / Folgesessions)
|
||||||
|
|
||||||
|
- 🔴 **`CloudService.cloud_networks` ist im Serializer `read_only` und bleibt bei GET leer** — die
|
||||||
|
Verknüpfung wird **ausschließlich** über `cloud-service-network-assignments` geschrieben UND gelesen.
|
||||||
|
Nicht am leeren Convenience-Feld verzweifeln; die Through-Rows sind die Wahrheit.
|
||||||
|
- VPC-Service-API liegt unter `connectivity/directory/services` (Update = **PUT**, voller Body), nicht `/vpc/`.
|
||||||
|
- Der Repo-`CF_API_TOKEN` darf `cfd_tunnel`, aber **nicht** Workers-VPC (`connectivity/directory`) — dort
|
||||||
|
über den `cloudflare`-MCP-Credential gehen.
|
||||||
|
> ⚠️ **ÜBERHOLT (2026-09-11):** falsch. Live getestet liest der `CF_API_TOKEN` aus
|
||||||
|
> `~/.secrets/credentials.env` Hyperdrive/VPC/Workers (alle 200) — der schmale ist `CF_DNS_API_TOKEN`.
|
||||||
|
> Der Teil-B-Sync deckt damit 14/14 ab. Details: `handoff-20260910-tp44-teilb-sync-live.md`.
|
||||||
|
|
||||||
|
## Offen (Etappe 2, eigene Session)
|
||||||
|
|
||||||
|
1. **CF-API→Nautobot-Sync** (Python/n8n): Inline-Daten im Seeder durch Live-Pull ersetzen, periodisch
|
||||||
|
(analog golden-config-bot). Dann ist Nautobot driftfrei.
|
||||||
|
2. ✅ **TP44 in `teilprojekte.md`** eingetragen (Quick-Index + Abschnitt + Ist-Stand) — 2026-09-06.
|
||||||
|
3. IPAM-Prefixe `10.40.0.0/16` + `10.101.0.0/24` anlegen → dann greifen die Prefix-Assignments automatisch.
|
||||||
|
4. ✅ Seeder-Datei nach `tp44-cf-estate-seed.py` umbenannt — 2026-09-06.
|
||||||
|
|
||||||
|
## Verifikation (2026-09-02)
|
||||||
|
|
||||||
|
Counts: CloudAccount 1 · ResourceType 6 · CloudNetwork 4 · CloudService 14 · Service-Network-Assignments 3.
|
||||||
|
Idempotenz-Gegenprobe grün.
|
||||||
57
docs/handoff-20260902-tp25-fund-e-messung.md
Normal file
57
docs/handoff-20260902-tp25-fund-e-messung.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Handoff 2026-09-02 — TP25 Fund E: die Abschlussmessung, und was sie entschieden hat
|
||||||
|
|
||||||
|
**Kurz:** Die für heute früh vorgemerkte Messung ist gelaufen. **7.24.1 hat den
|
||||||
|
`reauthenticating`-Sturm aufgelöst, nicht `ft-over-ds`.** Hebel 1 war widerlegt und ist zu Recht
|
||||||
|
zurückgebaut; Hebel 2–4 entfallen. **Fund E ist geschlossen** — ohne Geräte-Write.
|
||||||
|
|
||||||
|
## Was Fund E war
|
||||||
|
|
||||||
|
Ein `reauthenticating`-Sturm, sauber isoliert auf **einen** Client (iPhone
|
||||||
|
`F2:62:4C:E1:AF:09`) und **eine** SSID (`dirtyvader-main`, die einzige mit
|
||||||
|
`ft=yes ft-over-ds=yes`). Baseline: 93/93 auf genau diesem Client und dieser SSID. Hebel 1
|
||||||
|
(`ft-over-ds=no`) lief am 31.08. **6,4 h allein** und machte es **schlimmer** (124 ≈ 19/h + 2
|
||||||
|
erlebte Ausfälle) → am 01.09. zurückgebaut, Transaktion `TP25-fund-e-ft-over-ds` geschlossen.
|
||||||
|
Der Einbruch auf ~0/h fiel exakt auf das **TP27-Upgrade 7.24.1** (Core-Reboot 31.08. 14:36).
|
||||||
|
|
||||||
|
## Vorbedingung zuerst (CLAUDE.md-Gotcha: „grep caps + uptime vor Client-Diagnose")
|
||||||
|
|
||||||
|
- Core-Uptime **1d18h** → letzter Reboot 31.08. 14:36; **kein** Reboot am 01./02.09.
|
||||||
|
- `caps joined = 0` an beiden Tagen.
|
||||||
|
- ⇒ Die Zahlen unten sind echte Client-Ereignisse, keine Reboot-Artefakte.
|
||||||
|
|
||||||
|
## Messung (Kollektor `/opt/tp5-syslog/log/10.40.20.1/`, via pve→`pct exec 400`)
|
||||||
|
|
||||||
|
| Tag | reauth total | davon iPhone F2:62 | distinct MACs | data loss / deauth | Kontext |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| 08-31 | 225 | 218 | 6 | (Upgrade+Hebel1) | Hebel-1-Fenster + 2 Upgrade-Reboots |
|
||||||
|
| 09-01 | 44 | 40 | 4 | 0 | 7.24.1, Hebel 1 mittags zurückgebaut |
|
||||||
|
| **09-02** (00:00–08:37) | **4** | **4** | **1** | **0** | 7.24.1 + `ft-over-ds=yes`, sauberes Fenster |
|
||||||
|
|
||||||
|
- **218 → 40 → 4.** Am 09-02 vier isolierte Einzelereignisse (01:13, 02:00, 03:14, 08:01),
|
||||||
|
**kein Burst**, alle `dirtyvader-main` (Interface `cap-wifi3`/`cap-wifi6` — die `cap-wifiN`-Karte
|
||||||
|
wandert, deshalb NICHT einem AP zugeschrieben). ≈ **0,46/h** gegen Baseline 93 bzw. Hebel-1 **19/h**.
|
||||||
|
- Der iPhone ist **präsent und stabil** — er verbindet und erzeugt die 4 Zeilen, ist also nicht
|
||||||
|
„still, weil abwesend".
|
||||||
|
- Die 97 `association rejected` am 09-01 fallen auf den **Rollback-/Re-Provisioning-Vorgang**
|
||||||
|
(viele MACs = Infrastruktur, per Gotcha), nicht auf einen Client; am 09-02 nur 5.
|
||||||
|
|
||||||
|
## Schluss & Entscheidung
|
||||||
|
|
||||||
|
**7.24.1 war die Ursache/der Fix, nicht `ft-over-ds`.** Der Restwert (~0,5/h isolierte
|
||||||
|
Einzel-Reauth eines iPhones, 0 Ausfälle) ist normales iOS-Roaming, kein Defekt.
|
||||||
|
**Hebel 2–4 (`ft=no`, `2g-probe-delay`, `signal-range`) werden NICHT gezogen. Fund E geschlossen.**
|
||||||
|
|
||||||
|
Kein Geräte-Write: Sollzustand steht bereits (`sec-main` = `ft=yes ft-over-ds=yes`, Flotte 7.24.1,
|
||||||
|
Transaktion `zurueckgebaut`, Compliance **140/140**).
|
||||||
|
|
||||||
|
## Bleibt offen (nicht Fund E)
|
||||||
|
|
||||||
|
- **ap2 unterperformt** (2 m / −80 dBm, 23:02-Befund) — eigener In-situ-Unterpunkt in TP25,
|
||||||
|
kein FT-/Auth-Thema. TP25 bleibt deshalb als Sammel-TP offen.
|
||||||
|
|
||||||
|
## Merkregel bestätigt
|
||||||
|
|
||||||
|
Der Fall ist ein Lehrstück für zwei CLAUDE.md-Regeln: (a) **Reboots zuerst ausschließen** hätte am
|
||||||
|
31.08. verhindert, den Upgrade-Reboot-Burst für Client-Instabilität zu halten; (b) **eine Drift/ein
|
||||||
|
Symptom im offenen Versuch sieht aus wie ein Defekt** — Hebel 1 „verbesserte auf 0/h" war in
|
||||||
|
Wahrheit das parallele Upgrade. Die einzelne Messung nach dem Rückbau hat beides getrennt.
|
||||||
113
docs/handoff-20260903-tp25-tp28-kanalplan-messung.md
Normal file
113
docs/handoff-20260903-tp25-tp28-kanalplan-messung.md
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# Handoff 2026-09-03 — TP25/TP28: Kanalplan live, In-situ-Messung offen
|
||||||
|
|
||||||
|
**Kurz:** ap1 haengt jetzt am Sideboard (Trunk auf `ether3`), der TP25-Kanalplan ist am
|
||||||
|
Manager gesetzt (**ap1 5260/5280 + ch1**, **ap2 5300/5320 + ch11**, **ap3 5180 + ch6**),
|
||||||
|
und alle drei APs funken. 35 Clients an der Registration-Table (vorher 30, +5 auf ap1).
|
||||||
|
**Was fehlt: die In-situ-Messung** — Aufstellorte begehen und entscheiden, ob der Zustand
|
||||||
|
Zielzustand wird (dann Template + Commit der Akte) oder ob nachjustiert werden muss.
|
||||||
|
|
||||||
|
## Was am 03.09. live gesetzt wurde
|
||||||
|
|
||||||
|
L2 auf `crs326-core`:
|
||||||
|
- `ether3` als AP-Trunk fuer ap1 (analog `ether15`/`ether21`) — Template gepflegt
|
||||||
|
- Akte `TEST-ether3-trunk-ap1-sideboard-2` (Scope `bridge-vlan`, `state=committed`,
|
||||||
|
expires 2026-09-10)
|
||||||
|
|
||||||
|
WLAN am Manager (`crs326-core`):
|
||||||
|
- `ch-5g-ap1`: 5180 → **Frequenz-Liste 5260,5280** (DFS Band B, ch52/56)
|
||||||
|
- `ch-5g-ap2`: 5220 → **Frequenz-Liste 5300,5320** (DFS Band B, ch60/64)
|
||||||
|
- Zwei Provisioning-Regeln fuer ap1 neu (radio-mac `E5`=5G, `E6`=2G) — analog ap2/ap3
|
||||||
|
- `remote-cap provision` auf ap1 und ap2 gefahren
|
||||||
|
- Akte `TP25-kanalplan-ap1-live` (Scope `wifi-channel`, `state=open`, expires 2026-09-10)
|
||||||
|
|
||||||
|
Live-Verifikation:
|
||||||
|
- ap1: 9/9 Interfaces `running=true` am CAP (Manager-Anzeige `running=false` ist der bekannte
|
||||||
|
Post-Reprovisioning-Gotcha, s. CLAUDE.md — Wahrheit am CAP)
|
||||||
|
- ap2: 9/9 Interfaces `running=true`
|
||||||
|
- ap3: unveraendert
|
||||||
|
- 35 Clients (vor Aenderung 30)
|
||||||
|
|
||||||
|
## Was fuer die naechste Session zu tun ist
|
||||||
|
|
||||||
|
**Fokus = messen, nicht nachjustieren, bis die Aufstellorte belegt sind.**
|
||||||
|
|
||||||
|
Zwei offene Frage aus TP25/TP28, die vor Ort beantwortet werden muessen:
|
||||||
|
|
||||||
|
1. **Aufstellort ap1 (Sideboard)** — Abdeckung akzeptabel? Vorher stand ap1 im Technik UG, jetzt
|
||||||
|
am Sideboard (Test). Signal in den Ecken (Terrasse, Bad, hintere Zimmer) messen.
|
||||||
|
2. **ap2 unterperformt** (TP25 Rest-Punkt, In-situ 2 m/−80 dBm am 08-31). Vermutung: RF/Antenne/HW,
|
||||||
|
moeglicherweise auch nur `ch149`-Leistungsproblem (jetzt weg, 5300 statt 5220). Die
|
||||||
|
Kanalaenderung koennte das Symptom bereits behoben haben — deshalb **zuerst neu messen**,
|
||||||
|
bevor man an Antennen oder Hardware denkt.
|
||||||
|
|
||||||
|
## Aufstellorts-Messplan (Vorschlag, bewusst knapp)
|
||||||
|
|
||||||
|
Pro Standort (Wohnzimmer, Kueche, Bad, Terrasse, Schlafzimmer, Technik UG):
|
||||||
|
- Signal in dBm auf `dirtyvader-main` (5 GHz) mit App am Handy
|
||||||
|
- Welcher AP wird ausgewaehlt (BSSID am APx-Radio abgleichen — s. Manager-Registration-Table)
|
||||||
|
- Kurz eine Datei ziehen (SpeedTest oder `iperf3` gegen `10.40.20.10`), damit reale Throughput-
|
||||||
|
Zahl da ist, nicht nur RSSI
|
||||||
|
- 2,4 GHz nur pruefen, wenn das 5-GHz-Signal an dem Standort schlecht ist (< −75 dBm)
|
||||||
|
|
||||||
|
Am Manager parallel:
|
||||||
|
- `/interface wifi registration-table print terse where ssid=dirtyvader-main` — Signal/Rate live
|
||||||
|
- `/interface wifi radio print` gibt aktuelle Frequenz je Radio (5260 vs 5280, 5300 vs 5320 —
|
||||||
|
DFS hat einen der beiden gewaehlt)
|
||||||
|
|
||||||
|
## Gotchas fuer die Messsession (aus CLAUDE.md)
|
||||||
|
|
||||||
|
- **`/interface wifi scan` NUR am Manager**, nicht am CAP (`not allowed`).
|
||||||
|
- **Registration-Table NUR am Manager** — am CAP leer.
|
||||||
|
- **`cap-wifiN`-Karte wandert bei jedem Rejoin.** Vor der Auswertung eines Ereignisses
|
||||||
|
`/interface wifi radio print terse proplist=cap,radio-mac,interface` am Manager lesen; wenn
|
||||||
|
zwischen Ereignis und Auswertung ein Rejoin liegt, Zuordnung als **unbekannt** behandeln.
|
||||||
|
- **`list_wifi_interfaces` leakt PSKs** — nicht benutzen; stattdessen
|
||||||
|
`/interface wifi radio print` bzw. `/interface wifi print terse where master=yes proplist=...`.
|
||||||
|
- **Ein `caps,info`-Burst ist Infrastruktur, ein Client-MAC-Burst ist ein Client** — vor jeder
|
||||||
|
Client-Diagnose `/system resource get uptime` am Core lesen und `grep " caps,"` im TP5-Log.
|
||||||
|
- **DFS-CAC** dauert beim ersten Einschalten eines DFS-Kanals ~60 s. Bei einem Radar-Event
|
||||||
|
wechselt der Kanal automatisch auf den Zweitwert (5260↔5280 bzw. 5300↔5320) — deshalb steht
|
||||||
|
im Channel-Objekt eine **Liste**, kein Einzelwert.
|
||||||
|
|
||||||
|
## Rollback (falls die Messung schlecht ausfaellt)
|
||||||
|
|
||||||
|
Der Snapshot `docs/transaktionen/TP25-kanalplan-ap1-live.snapshot.rsc` haelt den Vorher-Zustand
|
||||||
|
(ap1=5180, ap2=5220, keine ap1-Provisioning-Regeln). Rollback per:
|
||||||
|
|
||||||
|
```
|
||||||
|
python3 nautobot/gc-transaktion.py rollback --change-id TP25-kanalplan-ap1-live
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Trunk auf `ether3` (physischer Umbau) muss separat via
|
||||||
|
`TEST-ether3-trunk-ap1-sideboard-2` behandelt werden.
|
||||||
|
|
||||||
|
## Commit-Weg (falls die Messung gut ausfaellt)
|
||||||
|
|
||||||
|
Zwei Sachen ins Template `nautobot/mikrotik_routeros.j2`:
|
||||||
|
1. Zeilen 206–207: `frequency=5180`/`5220` → Frequenz-Listen `5260,5280`/`5300,5320`
|
||||||
|
2. Nach den bestehenden Provisioning-Zeilen fuer ap2/ap3 die zwei ap1-Regeln einfuegen
|
||||||
|
(radio-mac `D0:EA:11:4D:40:E5` = 5G, `D0:EA:11:4D:40:E6` = 2G) — Slave-Set analog ap2/ap3
|
||||||
|
|
||||||
|
Danach:
|
||||||
|
```
|
||||||
|
python3 nautobot/gc-transaktion.py commit --change-id TP25-kanalplan-ap1-live
|
||||||
|
```
|
||||||
|
|
||||||
|
Skript verweigert den Commit, solange die SoT nicht nachgezogen ist — der Ablauf funktioniert
|
||||||
|
nur in dieser Reihenfolge.
|
||||||
|
|
||||||
|
## Ein-Scope-Grenze der Akte (wichtig, wenn Compliance-Status gelesen wird)
|
||||||
|
|
||||||
|
Die Akte deckt formell **nur `wifi-channel`**. Die zwei ap1-Provisioning-Adds fallen unter
|
||||||
|
`wifi-provisioning` — das begin-Skript erlaubt aber **nur eine offene Akte je Geraet** (Regel 3).
|
||||||
|
Deshalb steht im `commands.rsc` der Akte ein Header, der beides zusammenfuehrt. Im naechsten
|
||||||
|
Compliance-Run wird `wifi-provisioning` als „fremd" (nicht durch die aktive Akte gedeckt)
|
||||||
|
gemeldet werden — das ist **erklaerbarer, nicht undokumentierter Drift**. Beim Commit gehoert
|
||||||
|
BEIDES ins Template.
|
||||||
|
|
||||||
|
## Aufraeumkandidat, wenn alles glatt geht
|
||||||
|
|
||||||
|
- TP25-Ist-Stand-Block: Punkt „ap2 unterperformt" als geloest markieren (falls Messung das
|
||||||
|
bestaetigt).
|
||||||
|
- TP28-Ist-Stand-Block: „ap1 funkstill" ausbauen — ist mit 09-03 erledigt.
|
||||||
|
- Quick-Index-Zeile TP25 kann bei erfolgreichem Commit auf `✅` gesetzt werden.
|
||||||
70
docs/handoff-20260904-tp25-konsole-n-clients.md
Normal file
70
docs/handoff-20260904-tp25-konsole-n-clients.md
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
# Handoff 2026-09-04 — TP25: Klick-Konsole auf N Clients + Pin-UI
|
||||||
|
|
||||||
|
**Kurz:** Die Klick-Konsole (`docs/tp6-marker/server.py`, Port 3031) war beim Multi-Pin-Umbau
|
||||||
|
(2026-09-04, `tp25-pin-multi.sh` + `TP25_PHONES`) **nicht** mitgezogen worden — sie zeigte weiter
|
||||||
|
hartkodiert zwei Clients (iPhone/S22) und pinnte ueber die alte Gegen-AP-Automatik. Jetzt ist sie
|
||||||
|
auf **N Clients** generalisiert und hat eine **Pin-UI**: Signal/AP/Ping-Verlust je Client live,
|
||||||
|
Pinnen band-genau direkt in der Konsole. Die alte 2-Client-Automatik ist entfernt.
|
||||||
|
|
||||||
|
## Was umgebaut wurde
|
||||||
|
|
||||||
|
`docs/tp6-marker/server.py` (Hauptumbau):
|
||||||
|
- **Client-Liste aus Datei statt hartkodiert:** `geraete()` liest `geraete.txt` aus dem
|
||||||
|
Laufverzeichnis (schreibt `tp25-walk-messung.sh`, eine Zeile `name|ip|mac` je Client). Fallback
|
||||||
|
auf die zwei alten MACs fuer Alt-Laeufe.
|
||||||
|
- **Kein eigenes SSH mehr in der Konsole:** der `beobachter()`-Thread (2 MACs per SSH,
|
||||||
|
`beide-telefone.csv`) ist raus. Stattdessen liest die Konsole je Client die **letzte Zeile** von
|
||||||
|
`sampler-<name>.csv` (`sta_iface`, `sta_signal`) und den **juengsten Ping-Verlust** aus
|
||||||
|
`walk-ping-<name>.csv` (leeres `ms` = Verlust). Eine SSH-Session weniger gegen den Core-SPOF.
|
||||||
|
- **N Client-Karten** statt der festen iPhone/S22-Zeile: Name, AP (via `ap_map()`), Signal dBm,
|
||||||
|
Ping-Verlust der letzten ~30 s. **Alarm**, wenn ein **gepinnter** Client `sta_iface == "absent"`
|
||||||
|
ist → „<name> HAENGT NIRGENDS — nicht weitergehen" (Nachfolger des alten S22-Alarms).
|
||||||
|
- **Pin-UI** (Karte „Pinnen (Multi-Pin)"): je Client ein `<select>` mit `frei` / `apX/5GHz` /
|
||||||
|
`apX/2,4GHz` (Ziele aus `ap_map()` abgeleitet) plus drei Aktionen:
|
||||||
|
- **„Pin setzen"** → baut `TP25_PINSPEC="name|mac|ap|band;…"` aus den Nicht-`frei`-Auswahlen und
|
||||||
|
ruft `bash docs/tp25-pin-multi.sh set` im Thread.
|
||||||
|
- **„Pin loesen"** → `bash docs/tp25-pin-multi.sh off`.
|
||||||
|
- **„verify"** → `bash docs/tp25-pin-multi.sh verify`, Ausgabe als Statuszeile.
|
||||||
|
Neuer Endpunkt `POST /pinmulti` (analog zum alten `/pin`).
|
||||||
|
- **Entfernt:** `IPHONE_MAC`/`S22_MAC`, `_live`/`_auto`, `entscheide()`, `pin_lesen()`/
|
||||||
|
`pin_setzen()` (via `tp25-pin-s22.sh`), die ap1/ap2/auto-Buttons und der `PIN_SH`-Aufruf.
|
||||||
|
`tp25-pin-s22.sh` bleibt als Skript erhalten (liefert `map`), wird aber von der Konsole nicht
|
||||||
|
mehr zum Pinnen aufgerufen.
|
||||||
|
|
||||||
|
`docs/tp25-runde.sh`:
|
||||||
|
- `aufraeumen()` loest jetzt `tp25-pin-multi.sh off` statt `tp25-pin-s22.sh off` (Kommentar
|
||||||
|
angepasst: die Konsole pinnt nicht mehr automatisch).
|
||||||
|
|
||||||
|
## Bedienung (Messablauf)
|
||||||
|
|
||||||
|
```
|
||||||
|
bash docs/tp25-runde.sh [name] # Konsole (3031) + Messung, Adresse fuer das Telefon
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Am Telefon die Konsole oeffnen, einmal irgendwo tippen (schaltet den Signalton frei).
|
||||||
|
2. In der Konsole **pinnen**: je Client AP/Band waehlen, „Pin setzen". Clients ohne Ziel bleiben
|
||||||
|
frei (das Skript raeumt vorher alle `TP25-PINM-*`-Regeln ab).
|
||||||
|
3. Losgehen. 🚶 beim Losgehen, 📍 beim Stehenbleiben, Raum antippen. Alarm = ein gepinnter Client
|
||||||
|
haengt nirgends.
|
||||||
|
4. „⏹ Aufzeichnung stoppen" (oder Ctrl-C im Terminal). Der Wrapper loest den Multi-Pin beim Beenden.
|
||||||
|
|
||||||
|
Auswertung: `python3 docs/tp25-gehweg-auswertung.py --dir docs/messdaten/tp25-runde-<name>`
|
||||||
|
|
||||||
|
## Was noch offen ist (braucht die Geraete vor Ort)
|
||||||
|
|
||||||
|
- **Live-Test mit N Geraeten im Netz** (Plan-Verifikation Schritt 3): `TP25_PHONES="…" bash
|
||||||
|
docs/tp25-runde.sh test` → N Karten sichtbar, pinnen → `bash docs/tp25-pin-multi.sh verify` zeigt
|
||||||
|
die Regeln, gehen/Raeume/stoppen → Auswertung liefert die Raumtabelle mit N Geraeten.
|
||||||
|
- **Aufraeumen** (Schritt 4): nach dem Lauf `bash docs/tp25-pin-multi.sh off` — keine
|
||||||
|
`TP25-PINM-*`-Regeln am Manager zurueck.
|
||||||
|
|
||||||
|
Statisch verifiziert (ohne Geraete): AST-Parse ok, keine stale Referenzen auf die alte Automatik,
|
||||||
|
Smoke-Render mit 2 Test-Clients ok, `bash -n` auf allen drei Shell-Skripten ok.
|
||||||
|
|
||||||
|
## Gotchas (unveraendert, aus CLAUDE.md)
|
||||||
|
|
||||||
|
- **`cap-wifiN`-Karte wandert bei jedem Rejoin** — `tp25-walk-messung.sh` friert sie als
|
||||||
|
`ap-map.txt` im Messlauf ein; die Konsole beschriftet daraus.
|
||||||
|
- **Registration-Table nur am Manager** — die Sampler (`tp6-monitor-core.sh`) fragen den Core ab,
|
||||||
|
nicht die CAPs.
|
||||||
|
- **`sshpass -e`, nie `-p`** — gilt weiter fuer alle Skripte, die den Core ansprechen.
|
||||||
127
docs/handoff-20260906-tp37-34-36-plan.md
Normal file
127
docs/handoff-20260906-tp37-34-36-plan.md
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# Handoff 2026-09-06 — Plan TP37 (P1+2) → TP34, TP36 parallel
|
||||||
|
|
||||||
|
> **Sessiontyp:** „erst planen, nichts bauen" (User-Entscheid 2026-09-06). **Kein Nautobot-Write.**
|
||||||
|
> Dieses Dokument ist der verzahnte Runbook für die drei nachfolgenden Build-Sessions.
|
||||||
|
> Datenmodell-Entscheid: **native Nautobot-Modelle, keine eigene App** (konsistent zu TP44).
|
||||||
|
|
||||||
|
## 1. Ausgangslage — live gegen die API gelesen (2026-09-06)
|
||||||
|
|
||||||
|
| | Wert | Bedeutung |
|
||||||
|
|---|---|---|
|
||||||
|
| Nautobot | `http://10.40.20.30:8080`, v3.1.7 | Apps: **Golden Config 3.0.7 + Nornir** (cloud-Modelle sind Core) |
|
||||||
|
| Devices | 9 | 4 aktive MikroTik + decommissioned/Rest — in Build-Schritt 0 verifizieren |
|
||||||
|
| **VirtualMachines** | **0** | TP37 P1 offen |
|
||||||
|
| **Clusters** | **0** | TP37 P1 offen |
|
||||||
|
| **ipam.Service** | **0** | TP37 P2 offen |
|
||||||
|
| **CustomFields** | **0** | TP36 offen |
|
||||||
|
| Relationships / Tags | 0 / 0 | — |
|
||||||
|
| `cloud.*` (Account/ResType/Network/Service) | 1 / 6 / 4 / 14 | ✅ **TP44 Etappe 1** — CF-Estate steht |
|
||||||
|
|
||||||
|
**Kernbefund:** TP37 **Punkt 3 (CF-Connectoren) ist durch TP44 erledigt** (native cloud-Modelle). Echt
|
||||||
|
offen bleibt das **Substrat** (P1) und die **Dienste** (P2) — und genau darauf sitzt TP34.
|
||||||
|
|
||||||
|
## 2. Entscheidungen (User, 2026-09-06)
|
||||||
|
|
||||||
|
1. **Erst planen, nichts bauen** — diese Session schreibt nichts nach Nautobot.
|
||||||
|
2. **Native Modelle** — `virtualization.Cluster/VirtualMachine` + `ipam.Service`, **keine eigene App**.
|
||||||
|
3. **TP44-Doku aufgeräumt** — Quick-Index-Zeile + Abschnitt in `teilprojekte.md`, Seeder
|
||||||
|
`nautobot/tp36-…` → `tp44-cf-estate-seed.py` umbenannt.
|
||||||
|
|
||||||
|
## 3. Reihenfolge / Abhängigkeit (korrigiert)
|
||||||
|
|
||||||
|
```
|
||||||
|
TP37 P3 (CF-Connectoren) ─── ✅ ERLEDIGT via TP44 ───┐
|
||||||
|
│ gleiche native-Modell-Linie
|
||||||
|
TP37 P1 (Hosts/Container → VirtualMachine) │
|
||||||
|
└─► TP37 P2 (Dienste → ipam.Service) ─► TP34 (Caddyfile via GC)
|
||||||
|
|
||||||
|
TP36 (CustomFields auf den 4 Devices + Datei-Split) ── ∥ unabhängig, jederzeit parallel
|
||||||
|
```
|
||||||
|
|
||||||
|
**Grund für die Unabhängigkeit von TP36:** seine Live-Daten-CustomFields hängen an den **bestehenden
|
||||||
|
`dcim.Device`**, nicht an neuen VM/Service-Objekten. TP34 dagegen kann ohne die Backend-`ipam.Service`
|
||||||
|
aus P2 nichts rendern → **TP37 vor TP34** bleibt zwingend.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. TP37 Punkt 1 — Substrat: Hosts + Container als VMs
|
||||||
|
|
||||||
|
**Modell-Mapping (nativ):**
|
||||||
|
- `virtualization.ClusterType`: `Proxmox VE`, `Synology Docker`, `Docker Desktop`.
|
||||||
|
- `virtualization.Cluster`: `pve`, `nas-bf`, `macmini` (je einer, passender ClusterType).
|
||||||
|
*(Die physischen Hosts als eigene `dcim.Device` sind **optional** und kommen frühestens in einer
|
||||||
|
Etappe 2 — für den Doku-SSoT reicht der Cluster als Gruppierung. Empfehlung: Etappe 1 ohne Host-Devices.)*
|
||||||
|
- `virtualization.VirtualMachine` je Workload, mit `cluster` + `primary_ip4`:
|
||||||
|
- **pve:** alle LXCs (u.a. `platform-pg` 105/`.20.20`, Docker-LXC 400/`.20.126`, `mcast-relay` 108,
|
||||||
|
influxdb, MariaDB 302/`.20.23`, …).
|
||||||
|
- **nas-bf (DS923, Portainer ep14):** Nautobot-Stack-Container, ggf. Caddy/weitere.
|
||||||
|
- **macmini (Docker Desktop):** `n8n`/`n8n-redis`/`n8n-mcp`/`gitea-mcp` + terpene-Worker `:8890`.
|
||||||
|
|
||||||
|
**Build-Schritt 0 (read-only Inventur, BEVOR geseedet wird):** je Host die Workloads mit Name, Rolle,
|
||||||
|
primärer IP enumerieren — `pct list` + `pct config <id>` über **pve-mcp**, Portainer-API (ep14) für die
|
||||||
|
NAS, `docker ps` auf dem macmini. Ergebnis in eine **Inline-Datenstruktur** (Muster: `tp44-cf-estate-seed.py`)
|
||||||
|
→ idempotenter Seeder `nautobot/tp37-substrat-seed.py` (get-or-create, Dry-Run default, `--commit`).
|
||||||
|
|
||||||
|
## 5. TP37 Punkt 2 — Dienste als `ipam.Service`
|
||||||
|
|
||||||
|
- Je exponiertem Port ein `ipam.Service`, an die VM (bzw. später das Device) gebunden, das ihn betreibt:
|
||||||
|
Caddy `:80/:443`, AdGuard `:53` (bf1/bf2), Nautobot `:8080`, n8n `:5678`, HA `:8123`, Portainer `:9443`,
|
||||||
|
`pve-mcp :3000`, gitea, Vaultwarden, …
|
||||||
|
- **Quelle ohne Raten:** die Ports stehen in `CLAUDE.md` (Hardware/Tooling) **und** in den
|
||||||
|
`extra_config.origin/port`-Feldern der TP44-`CloudService`-Objekte — gegeneinander abgleichen.
|
||||||
|
- Idempotenter Seeder `nautobot/tp37-dienste-seed.py`.
|
||||||
|
|
||||||
|
## 6. 🔴 Auflage: Compliance-Gegenprobe (aus TP37 selbst)
|
||||||
|
|
||||||
|
Nach P1+2 **muss** `python3 nautobot/gc-transaktion.py status` bzw. `gc-configplan.py --refresh-compliance`
|
||||||
|
zeigen, dass die Geräte-Compliance **unverändert 140/140** ist. VMs/Services hinzuzufügen darf die
|
||||||
|
Golden-Config-Pipeline der 4 MikroTik **nicht** berühren. P1 und P2 sind je ein **abnehmbarer** Schritt
|
||||||
|
mit dieser Gegenprobe danach — nicht als Nebeneffekt von etwas anderem.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. TP34 — Caddyfile via Golden Config (nach P2)
|
||||||
|
|
||||||
|
- **Vorbedingung:** Backend-Dienste existieren als `ipam.Service` (P2).
|
||||||
|
- **Modell (nativ):** Proxy-Route = **Relationship** (oder CustomField) Frontend `caddy`-Service ↔ Backend-
|
||||||
|
Service, plus den öffentlichen Hostname am Backend. GC-Job + Jinja rendert den Caddyfile, Diff gegen
|
||||||
|
live `caddy-bf1`, neue ComplianceRule `caddyfile`.
|
||||||
|
- **Schritt 0 / Quick-Win (sobald gebaut wird):** `terpene.teilzeitladen.xyz → http://10.40.20.167:8890`
|
||||||
|
als **manueller** Caddy-Block, damit terpene läuft, bevor die GC-Pipeline steht.
|
||||||
|
- ⚠️ **Offener Design-Punkt:** Caddy ist **kein RouterOS-Gerät** — GC an ein Nicht-Netzgerät zu deployen
|
||||||
|
(Caddy-Reload) ist neu (die „nicht in TP37"-Grenze). Deploy-Pfad in der TP34-Build-Session entscheiden.
|
||||||
|
|
||||||
|
## 8. TP36 — Live-Daten-CustomFields + Datei-Split (parallel)
|
||||||
|
|
||||||
|
- **CustomFields auf `dcim.Device`:** `ros_version`, `compliance_pct`, `dhcp_server_count`, `mgmt_ip`,
|
||||||
|
`dns_upstream_mode`. Compliance-Zahl kommt aus **GC selbst**, der Rest aus MikroMCP/REST.
|
||||||
|
- **Skript `nautobot/gc-snapshot.py`** (neu — existiert noch nicht): liest live, setzt die CFs, rendert
|
||||||
|
`docs/teilprojekte-aktuell.md` als **read-only Snapshot**.
|
||||||
|
- **Datei-Split:** `teilprojekte-index.md` (nur aktive TPs, eine Zeile) · `teilprojekte-aktuell.md`
|
||||||
|
(Live-Snapshot) · `teilprojekte-archiv.md` (unverändert). `/projektstatus` liest **Index + Archiv**.
|
||||||
|
- **Unabhängig von TP37** → kann sofort/parallel starten.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Empfohlene Reihenfolge der Build-Sessions
|
||||||
|
|
||||||
|
1. **TP37 P1** (Substrat) — Inventur → Seeder → Compliance-Gegenprobe.
|
||||||
|
2. **TP37 P2** (Dienste) — schließt an P1 an, gleiche Gegenprobe.
|
||||||
|
3. **TP34** — nach P2 (Quick-Win terpene als Schritt 0).
|
||||||
|
4. **TP36** — jederzeit parallel zu 1–3.
|
||||||
|
|
||||||
|
## 10. Offene Entscheidungen je Build-Session (nicht heute)
|
||||||
|
|
||||||
|
- **TP34:** Proxy-Route-Modell (Relationship vs. CustomField) + Caddy-Deploy-Pfad (GC an Nicht-Netzgerät).
|
||||||
|
- **TP37:** Hosts als eigene `dcim.Device` anlegen oder nur Cluster? (Empfehlung: Etappe 1 nur Cluster.)
|
||||||
|
- **TP36:** genaues Feld-Set + wie oft `gc-snapshot.py` läuft (Bot analog golden-config vs. on-demand).
|
||||||
|
|
||||||
|
## 11. Was diese Session geändert hat (nur Doku)
|
||||||
|
|
||||||
|
- `nautobot/tp36-cf-estate-seed.py` → **`nautobot/tp44-cf-estate-seed.py`** umbenannt (+ 4 Referenzen,
|
||||||
|
+ Docstring-Titel TP36→TP44).
|
||||||
|
- `teilprojekte.md`: Quick-Index-Zeile TP44; Ist-Stand-Bullet 2026-09-06; TP34/TP36/TP37-Abschnitte auf
|
||||||
|
den korrigierten Stand gezogen; **neuer TP44-Abschnitt** mit Anker.
|
||||||
|
- `handoff-20260902-cf-estate-nautobot-ssot.md` + `cf-tunnel-mapping.md`: Dateiname-Referenzen + erledigte
|
||||||
|
TODOs nachgezogen.
|
||||||
|
- **TP23-Quick-Index-Zeile** (fremde, korrekte Fremd-Änderung ✅→⏳) auf User-Okay mit übernommen.
|
||||||
57
docs/handoff-20260910-tp44-teilb-sync-live.md
Normal file
57
docs/handoff-20260910-tp44-teilb-sync-live.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Handoff 2026-09-10 — TP44 Teil B: CF-Estate-Sync als Nautobot-Job LIVE
|
||||||
|
|
||||||
|
> **Ergebnis:** Der Cloudflare-Estate→Nautobot-Sync läuft als **täglicher Nautobot-Job**
|
||||||
|
> (`CFEstateSync`, grouping `TP44 / Cloudflare`). Dry-Run + Commit-Run sauber: **4 CloudNetworks /
|
||||||
|
> 14 CloudServices, alle CF-Endpoints lesbar, 0 Drift.** CF bleibt operativer Master, Nautobot ist
|
||||||
|
> read-only Dokumentations-SSoT. **TP44 ist damit vollständig durch.**
|
||||||
|
|
||||||
|
## Governance (User, 2026-09-09)
|
||||||
|
Periodische externe→Nautobot-Syncs leben als **Nautobot-Jobs** in `bf-infra/jobs/` und werden **in
|
||||||
|
Nautobot geplant** — kein per-Task-Container, kein Cron. Künftige Syncs = weitere Jobs in `jobs/`.
|
||||||
|
|
||||||
|
## Was live ist
|
||||||
|
- **Runtime:** `jobs/cf_estate_sync.py` (`CFEstateSync`, ORM). Pull der CF-API (urllib+Bearer),
|
||||||
|
Upsert per `extra_config`-Match (`tunnel_id`/`script`/`id`/`service_id`), Verwaisung = `stale`-Flag
|
||||||
|
statt Löschen. 403-tolerant (fehlender Scope kippt den Sync nicht; Verwaisung wird dann übersprungen).
|
||||||
|
- **Registrierung:** `jobs/__init__.py` ruft `register_jobs(CFEstateSync)`; **Repo-Wurzel-`__init__.py`**
|
||||||
|
macht `bf_infra` als reguläres Paket sichtbar.
|
||||||
|
- **Token:** Nautobot-Secret `cf-api-token` (Provider `environment-variable`) → `CF_API_TOKEN` am
|
||||||
|
`nautobot-worker`, verdrahtet im Anchor-`environment` von `docs/nas-vlan20/stacks/210-nautobot.yml`
|
||||||
|
(gitignored). Wert = der breite `CF_API_TOKEN` aus `~/.secrets/credentials.env` (liest 14/14).
|
||||||
|
- **Schedule:** `ScheduledJob "CF Estate Sync – taeglich"`, `interval=daily`, `start_time=2026-09-11T03:00:00Z`
|
||||||
|
(= 05:00 Europe/Vienna, vor den Golden-Config-Läufen), `enabled=True`, `dryrun=False`.
|
||||||
|
- **GitRepository:** `bf-infra` (`provided_contents=['extras.job']`) — war schon verdrahtet.
|
||||||
|
|
||||||
|
## Drei load-bearing Funde (sonst lädt/plant der Job nicht)
|
||||||
|
1. **Repo-Wurzel `__init__.py` ist Pflicht.** Nautobot 3.1.7 lädt Git-Repo-Jobs über
|
||||||
|
`import_modules_privately()` → `pkgutil.walk_packages([GIT_ROOT])`. walk_packages entdeckt **nur**
|
||||||
|
reguläre Pakete (mit `__init__.py`), **keine** Namespace-Packages. Ohne die Datei ist der geklonte
|
||||||
|
`GIT_ROOT/bf_infra/` unsichtbar → `jobs/` wird nie importiert. Symptom: Git-Sync **SUCCESS**, aber
|
||||||
|
Log „No jobs were registered on loading the `bf_infra.jobs` submodule" und **kein** Job-DB-Eintrag.
|
||||||
|
bf-infra ist das **erste** Git-Repo mit `extras.job` — daher nie zuvor aufgetreten. Der `module_path`-
|
||||||
|
Filter des Loaders importiert nur `bf_infra` + `bf_infra.jobs.*`; `nautobot/`, `routeros/` etc. bleiben
|
||||||
|
unberührt.
|
||||||
|
2. **`register_jobs()` gehört ins Paket-`__init__`**, nicht nur ins Submodul (Doku bestätigt).
|
||||||
|
3. **`Meta.has_sensitive_variables = False`** nötig — Default `True` ⇒ Nautobot verweigert das Scheduling
|
||||||
|
(„Job may have sensitive input variables"). Der Job nimmt nur `dryrun` (Boolean); das Token kommt aus
|
||||||
|
dem Secret, nicht aus einer Job-Variable.
|
||||||
|
|
||||||
|
## Zwei Daten-/Code-Funde beim Dry-Run
|
||||||
|
- **VPC-Bug:** VPC-Directory-Services tragen ihren Identifier in **`service_id`**, nicht `id`. Der Job las
|
||||||
|
`id` (=None) → per `.get(mk)==None` matchte er ein Fremdobjekt (App Launcher) und ließ den echten
|
||||||
|
`terpene-db` auf stale laufen. Fix: VPC nutzt `service_id`; `_upsert` überspringt `mv=None` generell.
|
||||||
|
- **Seed-Artefakt:** der geseedete CloudService `terpene-db` hatte `extra_config.service_id: null`
|
||||||
|
(der echte `019fa3d5-…` stand beim Seed nur in den Hyperdrive-Einträgen als `vpc_service_id`).
|
||||||
|
**Einmalig reconciled** (PATCH `service_id=019fa3d5-…`) → seither matcht der Sync sauber, 0 Drift.
|
||||||
|
|
||||||
|
## Drift-Korrektur (nicht stillschweigend)
|
||||||
|
Die frühere Notiz „Repo-`CF_API_TOKEN` darf kein Workers-VPC → über den `cloudflare`-MCP-Credential"
|
||||||
|
(TP44-Prosa + `handoff-20260902-…`) war **falsch**. Live getestet liest der `CF_API_TOKEN` aus
|
||||||
|
`~/.secrets/credentials.env` Hyperdrive/VPC/Workers (alle 200). Der schmale Token ist `CF_DNS_API_TOKEN`
|
||||||
|
(403). Der `cloudflare`-MCP ist der **remote OAuth-MCP** (kein API-Token) und für einen serverseitigen
|
||||||
|
Job ohnehin nicht nutzbar.
|
||||||
|
|
||||||
|
## Nächster CF-Sync = neuer Job im selben Muster
|
||||||
|
Modul in `jobs/` anlegen, Klasse importieren + `register_jobs(...)` im `jobs/__init__.py` ergänzen,
|
||||||
|
`Meta.has_sensitive_variables=False` setzen, Git-Sync, enablen, Dry-Run, dann Schedule. Die Wurzel-
|
||||||
|
`__init__.py` gilt schon.
|
||||||
28
docs/handoff-20260913-cf-api-token-rolled.md
Normal file
28
docs/handoff-20260913-cf-api-token-rolled.md
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Handoff 2026-09-13 — CF_API_TOKEN gerollt (Gitea bf-infra #69)
|
||||||
|
|
||||||
|
## Was passiert ist
|
||||||
|
|
||||||
|
`CF_API_TOKEN` (id `493ed8bd…`, konsolidiert) war nach TP41 tot (Fehler `1000` an allen drei
|
||||||
|
Fundorten). Statt eines separaten railplanner-Tokens wurde das konsolidierte Token am 2026-09-13
|
||||||
|
im CF-Dashboard **gerollt** (neuer Wert, gleicher Scope). Workers-Scope verifiziert:
|
||||||
|
`GET /accounts/{id}/workers/scripts` listet `railplanner` / `blaueblueten-n8n` / `terpene`.
|
||||||
|
|
||||||
|
## Drift behoben
|
||||||
|
|
||||||
|
Der neue Wert lag zunächst nur in `credentials.env`. Realignt auf identischen Wert in:
|
||||||
|
|
||||||
|
- `~/.secrets/exports.zsh` (SSoT) — vorher gar kein `CF_API_TOKEN`
|
||||||
|
- `~/.secrets/credentials.env`
|
||||||
|
- `bf-infra/mcp/cloudflare/.env` (vorher `…4148`)
|
||||||
|
- `bf-network-r6/mcp/cloudflare/.env` (vorher `…2174`)
|
||||||
|
|
||||||
|
## Gotcha (hat die Session zweimal „Token kaputt" fehldeuten lassen)
|
||||||
|
|
||||||
|
Account-owned Tokens verifizieren an `GET /accounts/{account_id}/tokens/verify` — am alten
|
||||||
|
`/user/tokens/verify` liefern sie `1000 "Invalid API Token"`, **obwohl gültig**. Siehe
|
||||||
|
`docs/tp16.1-warp-mesh-native-crs326.md:164` und `docs/teilprojekte-archiv.md:830`.
|
||||||
|
Ins Memory notiert (`reference_cf_account_token_verify.md`).
|
||||||
|
|
||||||
|
## Status
|
||||||
|
|
||||||
|
Gitea #69 `closed` (`status:done`). Kein Repo-Code geändert — alles Secret-Store + Gitea-Status.
|
||||||
111
docs/handoff-20260914-tp25-ap2-insitu-terrassse.md
Normal file
111
docs/handoff-20260914-tp25-ap2-insitu-terrassse.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# Handoff 2026-09-14 — TP25: ap2-In-situ-Messung (Terrasse), Hebel-A-Machbarkeitsprüfung
|
||||||
|
|
||||||
|
**Anlass:** Christoph beobachtete 19:18 auf der Terrasse springende WLAN-Balken (1→2→3 binnen
|
||||||
|
Sekunden) und zwei Anruf-Aussetzer; danach Umstellung des Telefons auf 5G-Zellular. Verdacht:
|
||||||
|
Das Telefon probe die APs ab und falle dabei hinaus.
|
||||||
|
|
||||||
|
**Beteiligte:** iPhone `F2:62:4C:E1:AF:09` (das bekannte Fund-E-Gerät), Manager `crs326-core`.
|
||||||
|
Alle Reads am Manager; **kein einziger Geräte-Write** in dieser Session.
|
||||||
|
|
||||||
|
## cap-wifi-Karte (verifiziert live 19:22, vor jeder Auswertung gelesen)
|
||||||
|
|
||||||
|
`cap-wifi1` = ap2 5G (5300/ax/Ce/DI, tx-power 18) · `cap-wifi6` = ap2 2,4G (2462, tx-power 15) ·
|
||||||
|
`cap-wifi2` = ap3 5G (5180) · `cap-wifi4` = ap3 2,4G · `cap-wifi3`/`cap-wifi5` = ap1 (5G 5280/2,4G).
|
||||||
|
⚠️ Das CLAUDE.md sagt „ap1 FUNKSTILL seit 08-30" — **überholt**: seit dem 09-04-Kanalplan-Commit
|
||||||
|
hat ap1 wieder eine Provisioning-Zeile und dient live (heute 16:51:53 sogar ein Radar-Event auf
|
||||||
|
5280 mit sauberem Kanalwechsel auf 5260/5280-Liste). Doku-Drift, hier vermerkt.
|
||||||
|
|
||||||
|
## Befund 1 — das Flapping ist echt und überwiegend band-intern an ap2
|
||||||
|
|
||||||
|
Kollektor-Log 18:53–19:18: **~30 Roams**, fast alle `cap-wifi1 ↔ cap-wifi6` = **ap2 5G ↔ ap2 2,4G**
|
||||||
|
(derselbe AP), Feldstärke −68…−81 dBm. Drei echte Abwürfe `disconnected, not responding`
|
||||||
|
(19:01:29, 19:12:10, 19:47:51 — der letzte tötete den Anruf). Ein Ausflug zu ap1-5G (19:35:45,
|
||||||
|
**−64**) und ap3 (−78…−82).
|
||||||
|
|
||||||
|
## Befund 2 — In-situ vor ap2: Hardware-Verdacht WIDERLEGT
|
||||||
|
|
||||||
|
Christoph stellte sich 1–2 m vor ap2. Registration-Table live (3 Stichproben + Log):
|
||||||
|
|
||||||
|
| Position | Interface | Signal | Verhalten |
|
||||||
|
|---|---|---|---|
|
||||||
|
| 1–2 m vor ap2 | cap-wifi1 (5G) | **−49…−51** | **>2 min stabil, 0 Roams** |
|
||||||
|
| Rückweg (Weg) | cap-wifi1 → cap-wifi6 | −76…−78 | 2 Roams binnen 26 s |
|
||||||
|
| Terrassenplatz | cap-wifi6 (2,4G) | **−65…−73** | 36 s im 1-s-Takt, 0 Roams, ±4 dB |
|
||||||
|
|
||||||
|
⇒ Der offene TP25-Unterpunkt „**ap2 unterperformt (2 m/−80, 23:02-Befund)**" ist **in situ
|
||||||
|
widerlegt**: ap2 sendet auf 2 m mit −49…−51 dBm und bleibt dort stabil. Der 23:02-Befund
|
||||||
|
reproduziert sich nicht — plausibel, weil seitdem der 09-04-Kanalplan (5220→5300/5320,
|
||||||
|
Band-B-Liste) committed ist; alternativ war die damalige Position/Attribution anders.
|
||||||
|
**ap2 RF/Antenne/HW: sauber. Kein Hardware-Tausch begründet.**
|
||||||
|
|
||||||
|
## Befund 3 — Ursache-Modell Terrasse: 5-GHz-Zellkante + client-getriebene Band-Aggressivität
|
||||||
|
|
||||||
|
- Terrassenplatz: 5 GHz **−76…−81** (Zellkante), 2,4 GHz **−65…−73** — die ~7 dB sind normale
|
||||||
|
Bandspreizung plus Wand. Link-Budget, kein Defekt.
|
||||||
|
- Das iPhone **bleibt**, sobald ein Band klar gewinnt (am Platz: 2,4 GHz, minutenlang ruhig).
|
||||||
|
Das Thrash passiert ausschließlich in der **Überlappungszone −70…−77**, wo beide Bänder
|
||||||
|
gleichauf liegen und 5 GHz ständig um die iOS-Aufgabeschwelle (~−75) pendelt.
|
||||||
|
- Client-Aggressivität als Second Layer: 19:44:21 stand das Telefon bei **−52 auf ap2-5G** und
|
||||||
|
floh 13 s später auf 2,4 (−77) — dasselbe Muster wie im Archiv-Fund E („Abbruch bei −47").
|
||||||
|
Steering-Objekte `st-main`/`st-guest` haben **keine Schwellen** (nur rrm/wnm) — der AP
|
||||||
|
drängt nichts aktiv per BTM, die Entscheidungen trifft das iPhone.
|
||||||
|
- Mit `ft=no` (Dauerzustand seit 09-04) ist jeder Roam eine Voll-Reassoziation in Sekundenhöhe
|
||||||
|
⇒ genau darin sterben Anrufe.
|
||||||
|
|
||||||
|
## Hebel-A-Machbarkeitsprüfung (Dry-Run, kein Write)
|
||||||
|
|
||||||
|
**Ziel:** 5-GHz-Boden für `dirtyvader-main` (reject unter ~−76), damit das Telefon an der
|
||||||
|
Zellkante sofort endgültig auf 2,4 GHz committet statt zu thrashen.
|
||||||
|
|
||||||
|
Live + MikroTik-Doku verifiziert:
|
||||||
|
- `signal-range` wird **kontinuierlich** geprüft (Grace über `allow-signal-out-of-range`,
|
||||||
|
Default 0s) — der Mechanismus trägt grundsätzlich.
|
||||||
|
- ABER: `/interface/wifi/access-list` hat **kein `band`-Property**. Band-Scoping nur über
|
||||||
|
`interface=` möglich — und die `cap-wifiN`-Namen wechseln bei jedem CAP-Rejoin (Gotcha
|
||||||
|
2026-08-31): die Regel würde stillschweigend auf ein falsches Radio zeigen (im Worst Case
|
||||||
|
ein 2,4-Master ⇒ Lockout-Schatten). Alternativen fallen alle durch:
|
||||||
|
- **Nur SSID scope** (ohne Band): trifft auch 2,4 GHz — Schlafzimmer-Gegenmessung 08-22
|
||||||
|
(−84 dBm am Counter-AP) ⇒ Lockout, exakt das 08-22-Gegenargument.
|
||||||
|
- **Nur MAC scope** (iPhone): trifft beide Bänder — das iPhone saß heute selbst bei −89 auf
|
||||||
|
2,4 GHz (19:01) ⇒ WiFi-Verlust in schwachen Räumen.
|
||||||
|
- **Fazit: Der Band-Boden ist in der aktuellen wifi-qcom-Access-List nicht sauber ausdrückbar.
|
||||||
|
Nicht umgesetzt; Dry-Run-Preview des Kandidaten gefahren, kein Apply.**
|
||||||
|
|
||||||
|
Kandidat (NICHT angewendet, nur als Referenz wie er *heute* aussehen würde):
|
||||||
|
|
||||||
|
```
|
||||||
|
/interface wifi access-list add interface=cap-wifi1 ssid-regexp=dirtyvader-main \
|
||||||
|
signal-range=-120..-76 action=reject comment="TP25-hebel-a-5g-boden"
|
||||||
|
```
|
||||||
|
(× cap-wifi1/2/3 — die drei 5G-Master — bricht bei jedem CAP-Rejoin)
|
||||||
|
|
||||||
|
**Verbleibende Hebel:**
|
||||||
|
1. **`ft=yes` uniform auf `sec-main`** — macht Roams nahtlos ⇒ Anrufe überleben das (verbleibende)
|
||||||
|
Springen. Bewusste Aufweichung der 09-04-Entscheidung (`ft=no`), Fund-E-Ursache (7.24.1) ist
|
||||||
|
längst behoben, FT-Flotte uniform. Ein Config-Weg: Template + Compliance + Transaktion.
|
||||||
|
2. **Deckungsfix (TP25 Variante 3)** — der eigentliche Hebel: ein AP muss an der Terrasse
|
||||||
|
eindeutig stärker sein als alle anderen. Bemerkenswert: ap1-5G erreichte die Terrasse heute
|
||||||
|
mit −64 (besser als ap2-5G mit −78) — ap1 sitzt bereits auf der Band-B-Liste 5260/5280.
|
||||||
|
3. Monitoring: wireless-Logging läuft persistent; das Flapping ist damit künftig rückwirkend
|
||||||
|
diagnostizierbar.
|
||||||
|
|
||||||
|
## Nachweise
|
||||||
|
|
||||||
|
- Live-Log am Manager: 43 + 18 + 8 Zeilen `wireless,info` zu `F2:62:4C:E1:AF:09`, 18:53–19:56.
|
||||||
|
- 1-s-Sampling: zwei 18-Sekunden-Loops (`:for` am Manager, Registration-Table), 20:01:44–20:02:26.
|
||||||
|
- Monitor-Reads: `cap-wifi1` → `5300/ax/Ce/DI tx-power 18`; `cap-wifi6` → `2462/ax tx-power 15`.
|
||||||
|
- Doku-Verifikation Access-List: help.mikrotik.com/docs/display/ROS/Wifi (signal-range kontinuierlich,
|
||||||
|
kein band-Property, first-match-wins, kein impliziter Reject).
|
||||||
|
|
||||||
|
## Offen / nächste Schritte
|
||||||
|
|
||||||
|
1. Entscheidung Christoph: `ft=yes` ja/nein (Hebel 1 oben) — ich lege dazu auf Go den
|
||||||
|
GC-ConfigPlan als Dry-Run vor.
|
||||||
|
2. Deckungsfix-Terminierung (Variante 3 / ap1-Endausbau) — TP25 bleibt offen.
|
||||||
|
3. CLAUDE.md-Drift „ap1 FUNKSTILL" beim nächsten CLAUDE.md-Edit korrigieren (nur Vermerk hier,
|
||||||
|
kein stiller Fix).
|
||||||
|
4. Gitea-TP25-Issue **#17** hat einen Kommentar mit diesem Befund erhalten.
|
||||||
|
5. 🔴 **Sicherheitsvorfall dieser Session:** durch einen Env-Parse-Fehler stand der
|
||||||
|
`GITEA_ACCESS_TOKEN` in einer URL im Traceback — **Token ist als kompromittiert zu
|
||||||
|
betrachten und zu rotieren** (neues Token erzeugen, `~/.hermes/.env` aktualisieren, altes
|
||||||
|
widerrufen). Siehe Merkregel-Klasse „Werte nie suchen, um sie zu sehen".
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Handoff 2026-09-15 — TP25: Steering-Threshold 5G deployed, Terrasse verifiziert, MacBook-Klebrigkeit „wird nicht umgesetzt"
|
||||||
|
|
||||||
|
**Anlass:** Folgesession zum TP25-Handoff „zuordnung-und-redundanz" ([[handoff-20260915-tp25-zuordnung-und-redundanz]]). Ziele: (1) Doku-Drift schließen, (2) Roaming-Threshold für die Terrasse (5G bricht früher zur 2,4G ab), (3) Terrasse-Nachmessung nach dem Deploy.
|
||||||
|
|
||||||
|
## 1. Steering-Threshold 5G — DEPLOYED, 140/140
|
||||||
|
|
||||||
|
**Problem:** Am Rundgang 09-15 saß der Terrasse-Client auf der **−80/5G-Kante** (genau der Default
|
||||||
|
`transition-threshold`); auf 2,4 GHz wäre das nicht passiert.
|
||||||
|
|
||||||
|
**Umsetzung (nur 5G):** neues Steering-Profil `st-main-5g` mit `transition-threshold=-72`
|
||||||
|
(statt Default −80) und `transition-threshold-time=5s` (statt 10s). Die drei 5G-Configs
|
||||||
|
(`cfg-main-5g-ap1/ap2/ap3`) zeigen darauf; die drei 2G-Configs bleiben auf `st-main` (unset −80),
|
||||||
|
weil 2,4 GHz draußen (Terrasse/außerhalb) Reichweite behalten soll. **8 dB Abstand = kleine Hysterese.**
|
||||||
|
|
||||||
|
Weg: Template-Edit (`nautobot/mikrotik_routeros.j2`) → `push-gc-template.py` (GC-Commits `6a4b504f`,
|
||||||
|
`242ed108`) → manueller Plan (`gc-manual-plan.py`, Plan `cc8340a8`, Completed) → Refresh:
|
||||||
|
**140/140, 0 Drifts.** Commands-File:
|
||||||
|
[`docs/transaktionen/TP25-steering-5g-threshold.commands.rsc`](transaktionen/TP25-steering-5g-threshold.commands.rsc).
|
||||||
|
|
||||||
|
## 2. Gotcha: Parameter-Reihenfolge im Template
|
||||||
|
|
||||||
|
Nach dem ersten Push meldete Compliance **139/140** mit einem `wifi-steering`-Drift: `extra`/`missing`
|
||||||
|
derselben Zeile, aber mit unterschiedlicher Feld-Reihenfolge. **RouterOS exportiert `transition-threshold`
|
||||||
|
vor `wnm` (alphabetisch)** — mein Template hatte `wnm` davor. Fix = Template-Zeile auf die Export-Reihenfolge
|
||||||
|
gebracht (`… rrm=yes transition-threshold=-72 transition-threshold-time=5s wnm=yes`). Kein Re-Deploy nötig
|
||||||
|
(das Gerät war schon korrekt, nur das Template war es nicht). **Merkregel: bei neuen Parametern im
|
||||||
|
`/interface wifi steering add` die RouterOS-Export-Reihenfolge (alphabetisch) verwenden, sonst Drift ohne
|
||||||
|
Fehlfunktion.**
|
||||||
|
|
||||||
|
## 3. Terrasse-Nachmessung — Threshold wirkt am iPhone, nicht am MacBook
|
||||||
|
|
||||||
|
Zwei Apple-Geräte 20 cm auseinander auf der Terrasse, beide auf `dirtyvader-main`, beide physisch
|
||||||
|
rein/raus bewegt (A/B):
|
||||||
|
|
||||||
|
| Gerät | Band | Signal | Roam-Verhalten |
|
||||||
|
|---|---|---|---|
|
||||||
|
| iPhone `F2:62:4C:E1:AF:09` | **2,4 GHz** | −55…−66 | ✅ roamt sauber (rein→5G, raus→2,4G) |
|
||||||
|
| MacBook `56:71:6C:22:E6:A9` | **5 GHz** | −73…−75 | ❌ kein Roam (uptime 1h15m unverändert) |
|
||||||
|
|
||||||
|
**Befund:** gleiche Bewegung, gegensätzliches Verhalten. iOS folgt dem Band-Wechsel; **macOS hält 5 GHz
|
||||||
|
klebrig fest** und ignoriert die 802.11v-Transition-Request — selbst unter der −72-Schwelle und beim
|
||||||
|
physischen Umhertragen. Christoph: das MacBook hatte an diesem Ort nie Probleme (−73/5G reicht ihm).
|
||||||
|
|
||||||
|
## 4. Entscheidung: MacBook-Klebrigkeit = ❌ WIRD NICHT UMGESETZT
|
||||||
|
|
||||||
|
Der einzige Hebel dagegen wäre `transition-time=immediate` (deauth-Erzwingung nach der
|
||||||
|
Transition-Request statt bloßem Vorschlag) — ein schärferer Eingriff mit Abbruch-Risiko für aktive
|
||||||
|
Sitzungen. **Christoph-Entscheid 2026-09-15: nicht umsetzen.** Kein Handlungsbedarf, da das MacBook
|
||||||
|
dort funktioniert. Nur dokumentiert, falls es später doch wieder aufkommt.
|
||||||
|
|
||||||
|
## 5. Doku-Drift geschlossen (diese Session)
|
||||||
|
|
||||||
|
- **CLAUDE.md:** ap1 „FUNKSTILL"→aktiv am Sideboard (`core ether3`), ap2→Technik UG (`core ether21`,
|
||||||
|
ether1-MAC `:39`), ap3→Technik OG (`branch ether24`), Branch-Portliste `24 ap3`, Gotcha-Zeile
|
||||||
|
ap2/ap3-Uplink. Kanäle mitkorrigiert (ap1 `5260/Ce`, ap2 `5300/Ce`).
|
||||||
|
- **teilprojekte.md:** neuer Stand-Block 2026-09-15 (definitive Zuordnung + Tausch-Befund ap2↔ap3).
|
||||||
|
- **Nautobot Location** (war `None`): ap1→`Wohnzimmer Sideboard`, ap2→`Technik UG`, ap3→`Technik OG`.
|
||||||
|
|
||||||
|
## 6. Handoff-Punkte (aus dem Vorgänger) — aufgelöst
|
||||||
|
|
||||||
|
1. **ap1 behalten/abbauen** → **behalten** (Christoph). APs bleiben an ihren jetzigen Orten.
|
||||||
|
2. **Tausch korrigieren** (cAP ax → Sideboard) → **verworfen**; aktueller Zustand bleibt.
|
||||||
|
3. **Drei-Räume-Schatten** nachmessen → **verworfen**; Signal war beim letzten Rundgang gut.
|
||||||
|
4. **Doku-Drift** → ✅ geschlossen (s. §5).
|
||||||
|
|
||||||
|
## Übergabeprompt (nächste Session)
|
||||||
|
|
||||||
|
> TP25: Steering-Threshold 5G deployed (st-main-5g, −72/5s, 140/140), Terrasse am iPhone verifiziert
|
||||||
|
> (2,4G statt 5G/−80). MacBook-Klebrigkeit auf 5G dokumentiert und per Christoph-Entscheid
|
||||||
|
> „wird nicht umgesetzt" (transition-time=immediate). Doku-Drift geschlossen, Handoff-Punkte aufgelöst
|
||||||
|
> (ap1 bleibt, APs bleiben, Drei-Räume verworfen). Siehe
|
||||||
|
> [[handoff-20260915-tp25-steering-threshold-und-terrasse]].
|
||||||
105
docs/handoff-20260915-tp25-tausch-und-zuordnung.md
Normal file
105
docs/handoff-20260915-tp25-tausch-und-zuordnung.md
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# Handoff 2026-09-15 — TP25: AP-Zuordnung geklärt, Messrunde (Vorher), Tausch ap1↔ap3 begonnen
|
||||||
|
|
||||||
|
**Anlass:** Christoph wieder on site in Linz. Zurückgestellter Job = TP25 Deckungsfix Variante 3
|
||||||
|
(„Deckenstrahler an den guten Platz, hAP in den Schatten"). Session lief als große Messrunde
|
||||||
|
(Vorher) + physischer Tausch ap1↔ap3. **Tausch ist physisch angefangen, aber der Netzzustand ist
|
||||||
|
zum Sitzungsende unklar — ap2 ist down.**
|
||||||
|
|
||||||
|
## 🔑 AP-Zuordnung — EINDEUTIG per physischem Test geklärt
|
||||||
|
|
||||||
|
Die Session lief lange mit vertauschter Zuordnung (auch Christoph hat sie zwischenzeitlich falsch
|
||||||
|
„korrigiert"). Der **Kabel-Test** hat sie eindeutig festgelegt:
|
||||||
|
|
||||||
|
| Gerät | IP | Position | Uplink |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **ap1** | 10.40.99.11 | **Sideboard / Wohnzimmer** | core `ether3` |
|
||||||
|
| **ap2** | 10.40.99.12 | **Technik OG** | core `ether8` → branch `ether24` |
|
||||||
|
| **ap3** | 10.40.99.13 | Technik UG | core `ether21` |
|
||||||
|
|
||||||
|
**Beweis:** Christoph zog das Kabel in Technik OG → `cap-wifi1`/`cap-wifi6` (= ap2,
|
||||||
|
Radio-MAC `D0:EA:11:4D:40:3E/3F`) verschwanden aus der cap-wifi-Karte, ap1/ap3 blieben.
|
||||||
|
⇒ ap2 = Technik OG, ap1 = Sideboard. Die ARP zeigt ap2 noch ~30 s „reachable" (Cache), die
|
||||||
|
CAPWAP-Karte ist die Wahrheit.
|
||||||
|
|
||||||
|
⚠️ **CLAUDE.md-Drift:** die Hardware-Tabelle sagt für ap1 „core ether15" — überholt, ap1 hängt an
|
||||||
|
`ether3`. Und die Doku-Aussage „ap1 FUNKSTILL seit 08-30" ist überholt (ap1 dient live).
|
||||||
|
|
||||||
|
## Messrunde (Vorher) — Methode
|
||||||
|
|
||||||
|
iPhone `F2:62:4C:E1:AF:09` (Fund-E-Gerät) wurde per **Access-List-Reject** gezielt auf je einen AP
|
||||||
|
gelenkt (die anderen zwei APs ablehnen), dann Registration-Table am Manager gelesen. Reject-Regeln
|
||||||
|
nur auf `ssid-regexp=dirtyvader-main`, am Ende immer `remove [find where comment~"TP25-messung"]`.
|
||||||
|
|
||||||
|
⚠️ **Zwei Methodik-Gotchas (neu):**
|
||||||
|
1. `interface=` in der Access-List nimmt **nur EINEN Wert** (keine Komma-Liste) — pro AP zwei
|
||||||
|
Regeln (5G- + 2,4G-Radio). Eine Komma-Liste wirft `input does not match any value of interface`.
|
||||||
|
2. **MAC-Tippfehler** `F2:62:4C:E1:41:AF:09` (ein „41" zu viel) ist mir **zweimal** passiert und
|
||||||
|
hat stille Fehl-Reads (leere Registration-Table) erzeugt. Korrekt ist `F2:62:4C:E1:AF:09`.
|
||||||
|
|
||||||
|
**Methodischer Vorbehalt:** die wiederholten Rejects + Disconnects drosseln die iOS-Wiederverbindung.
|
||||||
|
Ein „✗ tot"-Wert direkt nach mehreren Disconnects kann teils Artefakt sein — Christoph hat den
|
||||||
|
ap1-tot-Befund auf der Terrasse aber selbst per manuellen Verbindungsversuchen bestätigt.
|
||||||
|
|
||||||
|
## Messwerte (Vorher)
|
||||||
|
|
||||||
|
**Innenräume** (Durchschaltung, je Station alle 3 APs):
|
||||||
|
|
||||||
|
| Ort | ap1 (Sideboard) | ap2 (OG) | ap3 (UG) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Küche | 5G −52 | — | — |
|
||||||
|
| Schlafzimmer | 5G −52 | ✗ tot | 2,4G −57 / 5G −73 |
|
||||||
|
| Bad | 2,4G −71…−76 | ✗ tot | 5G −73 / 2,4G −72 |
|
||||||
|
| Klo | ✗ tot | 2,4G −75…−81 | 5G −69 |
|
||||||
|
| Technik OG | (−52 erst, dann ✗)† | 5G −47…−57 | 5G −68…−71 |
|
||||||
|
|
||||||
|
**Terrasse** (3 Runden je AP: sitzend → Rundgang entlang der Mauern → wieder sitzen):
|
||||||
|
|
||||||
|
| | ap1 (Sideboard) | ap2 (OG) | ap3 (UG) |
|
||||||
|
|---|---|---|---|
|
||||||
|
| Sitzend | ✗ tot | 5G −78…−85 / 2,4G −67 (Thrash) | ✗ tot |
|
||||||
|
| Rundgang | ✗ überall | 2,4G −63…−74 / 5G −73…−75 | ✗ (nur 1 Mauerdurchlass −70…−82) |
|
||||||
|
|
||||||
|
† Vorbehalt s.o.
|
||||||
|
|
||||||
|
**Kernbefunde:**
|
||||||
|
1. **Die Terrasse wird ausschließlich von ap2 (Technik OG) bedient — marginal** (Band-Thrash
|
||||||
|
5G −78…−85 / 2,4G −67). ap1 (Sideboard) erreicht die Terrasse **nicht**, ap3 nur an einem
|
||||||
|
Mauerdurchlass kurz (−70…−82). Die Prämisse „Sideboard-AP deckt die Terrasse" ist **widerlegt**.
|
||||||
|
2. **ap1 (Sideboard) ist der Innenraum-König:** Küche −52, Schlafzimmer −52, Bad −71…−76.
|
||||||
|
3. **ap3 (cAP ax, UG) hält als einziger beide Geschoße** (UG: Schlafzimmer/Bad/Klo −69…−73,
|
||||||
|
OG: Technik OG −68…−71 über die Decke).
|
||||||
|
|
||||||
|
## Tausch ap1↔ap3 — Status UNKLAR (Sitzungsende)
|
||||||
|
|
||||||
|
Christoph hat ap1 und ap3 physisch getauscht: **ap3 (cAP ax) liegt jetzt „am Kasten auf
|
||||||
|
ap1-Position"**, bewusst „oben/unten ausgerichtet wie Deckenmontage" (Abstrahlwinkel). Ports/VLAN
|
||||||
|
sollten laut Vorgabe gleich bleiben.
|
||||||
|
|
||||||
|
**Live-Befund direkt nach dem Tausch (Vorsicht, Zustand kann sich noch setzen):**
|
||||||
|
- ap2 = **down** (10.40.99.12 ARP `incomplete`, fehlt in cap-wifi-Karte). Vermutlich Kabel beim
|
||||||
|
Tausch abgezogen.
|
||||||
|
- ap1 = reachable, noch an `ether3` (Sideboard).
|
||||||
|
- ap3 = reachable, aber Bridge-Host zeigt es an **`ether8` (branch)**, nicht an `ether21`.
|
||||||
|
- `ether21` ist **leer**.
|
||||||
|
|
||||||
|
⇒ Der Tausch ist im Netz noch nicht so angekommen, wie physisch beabsichtigt. **Bevor irgendetwas
|
||||||
|
weitergemessen wird: ap2 wieder einstecken und die tatsächliche Verkabelung (welches Kabel an
|
||||||
|
welchem Gerät) klären.**
|
||||||
|
|
||||||
|
## Offen / nächste Schritte
|
||||||
|
|
||||||
|
1. **ap2 (Technik OG) wieder versorgen** — Kabel prüfen, ARP `10.40.99.12` wieder `reachable`.
|
||||||
|
2. **Verkabelung nach dem Tausch verifizieren:** Bridge-Host-Tabelle (`mac-address~"D0:EA:11"`)
|
||||||
|
gegen Soll (ap1=`ether3`, ap2=`ether8`/branch, ap3=`ether21`) stellen. ap3 hängt aktuell an
|
||||||
|
`ether8` — klären, ob das Absicht ist oder Verwechslung.
|
||||||
|
3. **Nachher-Messung** derselben Stationen (Terrasse + Innenräume), erst wenn 1+2 sauber sind.
|
||||||
|
4. **Nautobot hat keine Location/Position** für ap1/ap2/ap3 hinterlegt (`location: None`). Nach dem
|
||||||
|
abgeschlossenen Tausch die korrekten Positionen dort nachtragen (SSoT-Drift schließen).
|
||||||
|
5. Gitea-TP25-Issue #17 bekommt den Befund als Kommentar (Zitat auf Messwerte + Zuordnung).
|
||||||
|
|
||||||
|
## Messwerte-Kurzform (für Folgesession)
|
||||||
|
|
||||||
|
`cap-wifi`-Karte war am Sitzungsende nach ap2-Rejoin: `cap-wifi1`=ap3-5G, `cap-wifi2`=ap3-2,4G,
|
||||||
|
`cap-wifi3`=ap1-5G, `cap-wifi5`=ap1-2,4G, ap2 fehlte (down). **Die cap-wifiN-Nummern verschieben
|
||||||
|
sich bei jedem CAP-Rejoin — vor jeder Messung neu lesen** (`/interface wifi radio print terse
|
||||||
|
proplist=cap,radio-mac,interface` am Manager).
|
||||||
96
docs/handoff-20260915-tp25-zuordnung-und-redundanz.md
Normal file
96
docs/handoff-20260915-tp25-zuordnung-und-redundanz.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# Handoff 2026-09-15 — TP25: AP-Zuordnung fixiert, Tausch-Befund, ap1-Redundanz gemessen
|
||||||
|
|
||||||
|
**Anlass:** Folgesession zur unklaren Tausch-Lage vom Vormittag ([[handoff-20260915-tp25-tausch-und-zuordnung]]).
|
||||||
|
Ziele: (1) AP-Positionen definitiv per IP/MAC/Switch-Port klären, (2) ob die Messwerte den richtigen
|
||||||
|
Geräten zugeordnet sind, (3) Rundgang-Messung im natürlichen Zustand, (4) ap1-Redundanz-Test (ap1 aus).
|
||||||
|
|
||||||
|
## 🔑 AP-Zuordnung — DEFINITIV (IP + MAC + Switch-Port)
|
||||||
|
|
||||||
|
| Gerät | IP | ether1-MAC | Bridge/Mgmt-MAC | Radios (5G/2,4G) | Port | Position |
|
||||||
|
|---|---|---|---|---|---|---|
|
||||||
|
| **ap1** | .11 | `D0:EA:11:4D:40:E0` | `:E1` | `:E5` / `:E6` | core **ether3** | Sideboard (Wohnzimmer) |
|
||||||
|
| **ap2** | .12 | `D0:EA:11:4D:40:39` | `:3A` | `:3E` / `:3F` | core **ether21** | Technik **UG** |
|
||||||
|
| **ap3** | .13 | `D0:EA:11:FF:16:9D` | `:9E` | `:9F` / `:A0` | branch **ether24** | Technik **OG** |
|
||||||
|
|
||||||
|
Beweis: Neighbor-Tabelle (MNDP, L2) + Bridge-Host-Tabelle + Port-Link-Status am Core, live gelesen.
|
||||||
|
|
||||||
|
⚠️ **MAC-Korrektur:** ap2s `ether1` ist **`:39`**, nicht `:3A`. `:3A` ist die Bridge-/Mgmt-MAC
|
||||||
|
(die CLAUDE.md als „ap2" führt). ap2s Radios sind **`:3E`/`:3F`** — das Handoff-09-15 hatte damit
|
||||||
|
recht; meine frühere „um eins versetzt"-Korrektur war falsch (ich hatte `:3A` fälschlich als
|
||||||
|
ether1-Basis angenommen).
|
||||||
|
|
||||||
|
## Tausch-Befund — ap2↔ap3 statt ap1↔ap3
|
||||||
|
|
||||||
|
Geplant (Deckungsfix Variante 3): **ap1↔ap3** (cAP ax → Sideboard, hAP → UG-Schatten).
|
||||||
|
Tatsächlich getauscht: **ap2↔ap3**:
|
||||||
|
- **ap1** blieb Sideboard (ether3)
|
||||||
|
- **ap2** ging von OG → UG (ether21)
|
||||||
|
- **ap3** (cAP ax) ging von UG → OG (branch ether24)
|
||||||
|
|
||||||
|
⇒ Drei-Wege-Drehung mit dem **falschen Paar**. Der „Deckenstrahler" (cAP ax) steht jetzt in **OG**,
|
||||||
|
nicht am guten Platz (Sideboard). Ursache war die ap1/ap2-Verwechslung während der Session.
|
||||||
|
|
||||||
|
## Rundgang 1 — alle APs an, natürlicher Zustand (ohne Fixierung)
|
||||||
|
|
||||||
|
Methode: Registration-Table am Manager im 1-s-Takt, iPhone `F2:62:4C:E1:AF:09` auf `dirtyvader-main`.
|
||||||
|
|
||||||
|
- **0 Disconnects**, Signal **−45…−77 dBm** (alles brauchbar, meist deutlich besser).
|
||||||
|
- Roaming natürlich über **alle 3 APs** (ap3→ap2→ap1→ap2→ap3) — funktioniert.
|
||||||
|
- **Terrasse** (das Sorgenkind): **−58 dBm stabil** an OG-Position (jetzt cAP ax), 2,4G.
|
||||||
|
- **ap1 (Sideboard)** wurde nur **kurz gestreift** (−52…−60); den Rest dominieren ap3 (OG) + ap2 (UG).
|
||||||
|
- Kein Band-Thrash: nur ein sinnvoller 2,4G-Ausflug bei 5G-Zellkante (−77), sonst sauber 5G.
|
||||||
|
|
||||||
|
## Rundgang 2 — ap1 komplett aus (Redundanz-Test)
|
||||||
|
|
||||||
|
**Methode (neues CAPsMAN-Wissen):** ap1-Radios lassen sich **nicht** über `cap-wifiN` abschalten
|
||||||
|
(`failure: can not change dynamic interface`). Richtig: Provisioning-Regeln auf `action=none`, dann
|
||||||
|
re-provisionieren:
|
||||||
|
```
|
||||||
|
/interface wifi provisioning set [find where radio-mac=D0:EA:11:4D:40:E5 or radio-mac=D0:EA:11:4D:40:E6] action=none
|
||||||
|
/interface wifi capsman remote-cap provision [find where identity="ap1"]
|
||||||
|
```
|
||||||
|
Rückbau = `action=create-dynamic-enabled` + dieselbe re-provision. (Merkregel: `cap-wifiN` sind
|
||||||
|
dynamische Interfaces; Abschalten geht nur über die Provisioning-Regel + re-provision.)
|
||||||
|
|
||||||
|
**Ergebnis:**
|
||||||
|
- **Main-Client (iPhone):** ohne ap1 voll abgedeckt (ap2+ap3), tiefster Wert **−80 dBm** (ap3/OG 5G,
|
||||||
|
an der Terrasse — dieselbe bekannte Zellkante, nur diesmal auf 5G statt 2,4G erwischt), kein Disconnect.
|
||||||
|
- **IOT:** vorher 27 Geräte (19 ap1 + 8 ap2). Nach Abschalten **alle 27 zurück** — 23 auf ap2, 4 auf ap3.
|
||||||
|
MAC-gegengeprüft, keiner verloren. **Aber ~4 Geräte sitzen marginal bei −77…−80 dBm** (Wohnzimmer-Geräte,
|
||||||
|
vorher nah am Sideboard-AP, jetzt an der ap2/UG-2,4G-Zellkante).
|
||||||
|
- **ap1 wieder eingeschaltet** (Revert) — Zustand vor dem Test wiederhergestellt.
|
||||||
|
|
||||||
|
## Befund: ap1 technisch redundant, aber 4 IOT-Geräte an der Kante
|
||||||
|
|
||||||
|
| Aspekt | Ergebnis |
|
||||||
|
|---|---|
|
||||||
|
| Main-Client | ✅ ohne ap1 voll abgedeckt, tiefster Wert −80, kein Disconnect |
|
||||||
|
| IOT (27) | ✅ alle verbunden, **aber ~4 marginal −77…−80 dBm** |
|
||||||
|
|
||||||
|
ap1 ist **technisch redundant** — nichts fällt aus, wenn es weg ist. Aber die ~4 Wohnzimmer-IOT-Geräte
|
||||||
|
verlieren ihre Signal-Reserve (verbunden ja, Aussetzer-Risiko erhöht). „ap1 abbauen" ist eine echte
|
||||||
|
Option, aber kein Selbstläufer.
|
||||||
|
|
||||||
|
## Offen / nächste Schritte
|
||||||
|
|
||||||
|
1. **Entscheidung ap1:** behalten (Status quo) oder Richtung Abbau? Nur sinnvoll, wenn die ~4 marginalen
|
||||||
|
IOT-Geräte akzeptiert werden (oder deren Platzierung/ap2-Abdeckung verbessert wird).
|
||||||
|
2. **Tausch korrigieren?** Aktuell cAP ax @ OG, hAP @ UG, hAP @ Sideboard — misst gut, ist aber nicht der
|
||||||
|
geplante Zustand (cAP ax → Sideboard). Der **Drei-Räume-Schatten** (Schlafzimmer/Bad/Küche, 08-22:
|
||||||
|
−82…−84) ist mit dem jetzigen Arrangement **noch nicht verifiziert** — der Rundgang lief ohne
|
||||||
|
Raumnamen/Fixierung. Nächste Messung sollte die drei Räume gezielt prüfen.
|
||||||
|
3. **Doku-Drift schließen** (nicht in dieser Session angefasst): CLAUDE.md-Hardware-Tabelle (ap2-MAC
|
||||||
|
`:3A`→`:39` ether1, ap2 Position OG→UG, ap3 UG→OG, ap1 „FUNKSTILL" überholt), teilprojekte.md TP25-Abschnitt,
|
||||||
|
Nautobot Location (nach wie vor `None`).
|
||||||
|
4. Gitea-Issue #17 trägt den Befund als Kommentar.
|
||||||
|
|
||||||
|
## Übergabeprompt (nächste Session)
|
||||||
|
|
||||||
|
> TP25 weiter: Die AP-Zuordnung ist jetzt definitiv geklärt (Handoff 2026-09-15 „zuordnung-und-redundanz").
|
||||||
|
> ap1=Sideboard (core ether3), ap2=Technik UG (core ether21), ap3=Technik OG (branch ether24) — der Tausch
|
||||||
|
> hat ap2↔ap3 vertauscht statt ap1↔ap3, ap1 blieb stehen. Rundgang ergab: Coverage überall gut (0 Disconnects,
|
||||||
|
> −45…−77 dBm), Terrasse −58 stabil an OG, ap1 nur kurz gestreift. ap1-Aus-Test: Main-Client voll abgedeckt,
|
||||||
|
> alle 27 IOT-Geräte kehren zurück, aber ~4 Wohnzimmer-IOT sitzen marginal −77…−80 dBm. ap1 ist wieder an.
|
||||||
|
> Offen: (1) ap1 behalten oder abbauen? (2) Tausch auf Plan korrigieren (cAP ax → Sideboard)? (3) Drei-Räume-
|
||||||
|
> Schatten (Schlafzimmer/Bad/Küche) gezielt nachmessen. (4) Doku-Drift (CLAUDE.md MAC/Positionen, Nautobot
|
||||||
|
> Location) schließen.
|
||||||
@@ -68,7 +68,7 @@ den *damals geplanten* Werten und ⬜-Status. Sie sind erledigt bzw. gegenstands
|
|||||||
| ~~FritzBox 6660~~ | — | WAN/Transfer | — | — | ❌ **physisch entfernt** | Liwest-Kabelmodemtausch 2026-07-01 (Edge-Cutover), kein Zurück — Arris-Kabelmodem im Bridge-Mode ersetzt sie komplett |
|
| ~~FritzBox 6660~~ | — | WAN/Transfer | — | — | ❌ **physisch entfernt** | Liwest-Kabelmodemtausch 2026-07-01 (Edge-Cutover), kein Zurück — Arris-Kabelmodem im Bridge-Mode ersetzt sie komplett |
|
||||||
|
|
||||||
| platform-pg (LXC 105, pve) | — (neu) | Platform-Postgres (TP20), PG16, DB `nautobot` | 20 INFRA | `10.40.20.20` | ✅ **live** | TP20 2026-07-16: erstellt via `pct create`, Ubuntu 24.04 (native PG16, kein PGDG-Repo nötig), UTF8/en_US.UTF-8, `wal_level=replica`, `archive_mode` bewusst noch aus (s. Memory) bis pgBackRest/Garage-S3 da ist; Creds `postgres/.secrets` |
|
| platform-pg (LXC 105, pve) | — (neu) | Platform-Postgres (TP20), PG16, DB `nautobot` | 20 INFRA | `10.40.20.20` | ✅ **live** | TP20 2026-07-16: erstellt via `pct create`, Ubuntu 24.04 (native PG16, kein PGDG-Repo nötig), UTF8/en_US.UTF-8, `wal_level=replica`, `archive_mode` bewusst noch aus (s. Memory) bis pgBackRest/Garage-S3 da ist; Creds `postgres/.secrets` |
|
||||||
| nautobot (Portainer-Stack, NAS) | — (neu) | Nautobot Golden Config (TP21), App/API | 20 INFRA | `10.40.20.30` | ✅ **live** | TP21 2026-07-16: `ghcr.io/nautobot/nautobot:3.1.7`, ep14 Stack Id 203, DB=platform-pg; Begleitcontainer `nautobot-worker`.20.31, `nautobot-scheduler`.20.32, `nautobot-redis`.20.34 |
|
| nautobot (Portainer-Stack, NAS) | — (neu) | Nautobot Golden Config (TP21), App/API | 20 INFRA | `10.40.20.154` | ✅ **live** | TP21 2026-07-16: `ghcr.io/nautobot/nautobot:3.1.7`, ep14 Stack Id 203, DB=platform-pg; Begleitcontainer `nautobot-worker`.20.155, `nautobot-scheduler`.20.156, `nautobot-redis`.20.158. **TP45 Schicht A 2026-09-21:** Oktett-Klasse `.150–.199` (App-Services) — die alten `.30–.34` gibt Nautobot an AGH `.30`/`.31` frei |
|
||||||
|
|
||||||
> Weitere Geräte (Home Assistant, HA-IoT, Drucker-Reservierung, etc.) hier ergänzen, sobald relevant.
|
> Weitere Geräte (Home Assistant, HA-IoT, Drucker-Reservierung, etc.) hier ergänzen, sobald relevant.
|
||||||
|
|
||||||
|
|||||||
6
docs/messdaten/tp25-runde-1er/ap-map.txt
Normal file
6
docs/messdaten/tp25-runde-1er/ap-map.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
cap-wifi1=ap2/5GHz
|
||||||
|
cap-wifi2=ap3/5GHz
|
||||||
|
cap-wifi3=ap1/5GHz
|
||||||
|
cap-wifi4=ap3/2,4GHz
|
||||||
|
cap-wifi5=ap1/2,4GHz
|
||||||
|
cap-wifi6=ap2/2,4GHz
|
||||||
2
docs/messdaten/tp25-runde-1er/geraete.txt
Normal file
2
docs/messdaten/tp25-runde-1er/geraete.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
iphone|10.40.10.196|F2:62:4C:E1:AF:09
|
||||||
|
s22|10.40.10.178|2A:0D:B0:5D:AD:A7
|
||||||
6
docs/messdaten/tp25-runde-1er/markers.csv
Normal file
6
docs/messdaten/tp25-runde-1er/markers.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
ts_epoch,ts_local,marker
|
||||||
|
1788526676,14:57:56,PINM=off
|
||||||
|
1788526735,14:58:55,PINM=off
|
||||||
|
1788526763,14:59:23,PINM=off
|
||||||
|
1788526765,14:59:25,PINM=off
|
||||||
|
1788526770,14:59:30,PINM=s22|2A:0D:B0:5D:AD:A7|ap1|24
|
||||||
|
1
docs/messdaten/tp25-runde-1er/phase.json
Normal file
1
docs/messdaten/tp25-runde-1er/phase.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"mode": "langzeit", "phase": "GEHWEG", "title": "Roaming-Messung l\u00e4uft", "instruction": "Aufzeichnung gestoppt.", "step": 0, "steps": 0, "ends": 0}
|
||||||
26
docs/messdaten/tp25-runde-1er/sampler-iphone.csv
Normal file
26
docs/messdaten/tp25-runde-1er/sampler-iphone.csv
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788526644,14:57:24,14:57:24,41,472232,195392,cap-wifi3,-57,309700000,325000000,2800,2960,00:04:37,00:00:00
|
||||||
|
1788526646,14:57:26,14:57:26,11,229864,36304,cap-wifi3,-57,309700000,325000000,2800,2960,00:04:39,00:00:00
|
||||||
|
1788526648,14:57:28,14:57:28,8,132384,26968,cap-wifi3,-59,309700000,309700000,6160,6512,00:04:41,00:00:00
|
||||||
|
1788526650,14:57:30,14:57:30,30,196672,48240,cap-wifi3,-59,309700000,344100000,6160,6512,00:04:43,00:00:00
|
||||||
|
1788526652,14:57:32,14:57:32,12,131304,37448,cap-wifi3,-59,309700000,344100000,6160,6512,00:04:45,00:00:00
|
||||||
|
1788526654,14:57:34,14:57:34,11,92000,18872,cap-wifi3,-59,309700000,412900000,5600,5920,00:04:47,00:00:00
|
||||||
|
1788526656,14:57:36,14:57:36,12,130144,24840,cap-wifi3,-60,309700000,390000000,6048,6512,00:04:49,00:00:00
|
||||||
|
1788526658,14:57:38,14:57:38,9,105160,20800,cap-wifi3,-60,309700000,390000000,6048,6512,00:04:51,00:00:00
|
||||||
|
1788526660,14:57:40,14:57:40,14,177144,59112,cap-wifi3,-57,309700000,344100000,6048,6288,00:04:53,00:00:00
|
||||||
|
1788526662,14:57:42,14:57:42,11,136152,39360,cap-wifi3,-59,309700000,325000000,5600,5920,00:04:55,00:00:00
|
||||||
|
1788526664,14:57:44,14:57:44,7,132416,46304,cap-wifi3,-59,309700000,325000000,5600,5920,00:04:57,00:00:00
|
||||||
|
1788526666,14:57:46,14:57:46,8,462016,41704,cap-wifi3,-60,309700000,390000000,5600,6296,00:04:59,00:00:00
|
||||||
|
1788526668,14:57:48,14:57:48,11,129736,41416,cap-wifi3,-59,309700000,390000000,5600,5920,00:05:01,00:00:00
|
||||||
|
1788526670,14:57:50,14:57:51,14,401784,54464,cap-wifi3,-59,309700000,390000000,5600,5920,00:05:03,00:00:00
|
||||||
|
1788526672,14:57:52,14:57:53,8,146064,92728,cap-wifi3,-56,309700000,433300000,6640,7008,00:05:05,00:00:00
|
||||||
|
1788526674,14:57:54,14:57:55,14,111952,31488,cap-wifi3,-59,309700000,433300000,6160,6512,00:05:07,00:00:00
|
||||||
|
1788526677,14:57:57,14:57:57,9,123096,20552,cap-wifi3,-57,309700000,433300000,6160,6512,00:05:09,00:00:00
|
||||||
|
1788526679,14:57:59,14:57:59,15,113008,17656,cap-wifi3,-57,309700000,433300000,6160,6512,00:05:11,00:00:00
|
||||||
|
1788526681,14:58:01,14:58:01,16,172400,59040,cap-wifi3,-58,309700000,433300000,6160,6512,00:05:14,00:00:00
|
||||||
|
1788526683,14:58:03,14:58:03,10,190968,57448,cap-wifi3,-59,309700000,433300000,5600,5920,00:05:16,00:00:00
|
||||||
|
1788526685,14:58:05,14:58:05,8,183848,56872,cap-wifi3,-59,309700000,433300000,5600,5920,00:05:18,00:00:00
|
||||||
|
1788526687,14:58:07,14:58:07,9,305320,47576,cap-wifi3,-59,309700000,487500000,5600,5920,00:05:20,00:00:00
|
||||||
|
1788526689,14:58:09,14:58:09,20,331408,59856,cap-wifi3,-58,309700000,455800000,5600,5920,00:05:22,00:00:00
|
||||||
|
1788526691,14:58:11,14:58:11,10,322448,65576,cap-wifi3,-58,309700000,455800000,5600,5920,00:05:24,00:00:00
|
||||||
|
1788526692,14:58:12,RECONNECT
|
||||||
|
1
docs/messdaten/tp25-runde-1er/sampler-iphone.out
Normal file
1
docs/messdaten/tp25-runde-1er/sampler-iphone.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-1er/sampler-iphone.csv (Takt 2s, Client F2:62:4C:E1:AF:09)
|
||||||
1
docs/messdaten/tp25-runde-1er/sampler-iphone.stderr.log
Normal file
1
docs/messdaten/tp25-runde-1er/sampler-iphone.stderr.log
Normal file
@@ -0,0 +1 @@
|
|||||||
|
14:58:12 Verbindung weg -- neuer Versuch in 2 s
|
||||||
26
docs/messdaten/tp25-runde-1er/sampler-s22.csv
Normal file
26
docs/messdaten/tp25-runde-1er/sampler-s22.csv
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788526644,14:57:24,14:57:24,41,472232,195392,cap-wifi5,-61,137600000,229400000,2800,2960,01:04:55,00:00:00
|
||||||
|
1788526646,14:57:26,14:57:26,11,229864,36304,cap-wifi5,-59,172100000,229400000,2800,2960,01:04:57,00:00:00
|
||||||
|
1788526648,14:57:28,14:57:28,8,143088,22488,cap-wifi5,-60,172100000,229400000,2800,2368,01:04:59,00:00:00
|
||||||
|
1788526650,14:57:30,14:57:30,30,192288,49440,cap-wifi5,-60,172100000,229400000,2800,2368,01:05:01,00:00:00
|
||||||
|
1788526652,14:57:32,14:57:32,12,131304,37448,cap-wifi5,-60,172100000,229400000,3360,3552,01:05:03,00:00:00
|
||||||
|
1788526654,14:57:34,14:57:34,11,92000,18872,cap-wifi5,-60,172100000,229400000,2800,2960,01:05:05,00:00:00
|
||||||
|
1788526656,14:57:36,14:57:36,12,130144,24840,cap-wifi5,-60,172100000,229400000,2800,2960,01:05:07,00:00:00
|
||||||
|
1788526658,14:57:38,14:57:38,9,105160,20800,cap-wifi5,-59,129000000,258100000,3856,2960,01:05:09,00:00:00
|
||||||
|
1788526660,14:57:40,14:57:40,14,177144,59112,cap-wifi5,-58,114700000,229400000,7888,15664,01:05:11,00:00:00
|
||||||
|
1788526662,14:57:42,14:57:42,11,136152,39360,cap-wifi5,-58,114700000,229400000,7888,15664,01:05:13,00:00:00
|
||||||
|
1788526664,14:57:44,14:57:45,7,132416,46304,cap-wifi5,-57,114700000,206500000,3360,2960,01:05:15,00:00:00
|
||||||
|
1788526666,14:57:46,14:57:47,8,462016,41704,cap-wifi5,-57,114700000,206500000,2800,2960,01:05:17,00:00:00
|
||||||
|
1788526669,14:57:49,14:57:49,11,129736,41416,cap-wifi5,-57,154900000,172100000,3360,3552,01:05:19,00:00:00
|
||||||
|
1788526671,14:57:51,14:57:51,14,401784,54464,cap-wifi5,-57,154900000,172100000,3360,3552,01:05:21,00:00:00
|
||||||
|
1788526673,14:57:53,14:57:53,8,146064,92728,cap-wifi5,-58,154900000,206500000,2800,2960,01:05:23,00:00:00
|
||||||
|
1788526675,14:57:55,14:57:55,14,111952,31488,cap-wifi5,-58,154900000,229400000,2800,2960,01:05:26,00:00:00
|
||||||
|
1788526677,14:57:57,14:57:57,9,123096,20552,cap-wifi5,-58,154900000,229400000,2800,2960,01:05:28,00:00:00
|
||||||
|
1788526679,14:57:59,14:57:59,17,113008,17656,cap-wifi5,-58,103200000,229400000,2800,2960,01:05:30,00:00:00
|
||||||
|
1788526681,14:58:01,14:58:01,9,172400,59040,cap-wifi5,-58,103200000,206500000,3248,3328,01:05:32,00:00:00
|
||||||
|
1788526683,14:58:03,14:58:03,10,190968,57448,cap-wifi5,-58,103200000,206500000,3248,3328,01:05:34,00:00:00
|
||||||
|
1788526685,14:58:05,14:58:05,10,183848,56872,cap-wifi5,-58,103200000,206500000,2800,2960,01:05:36,00:00:00
|
||||||
|
1788526687,14:58:07,14:58:07,9,305320,47576,cap-wifi5,-59,103200000,229400000,2800,2960,01:05:38,00:00:00
|
||||||
|
1788526689,14:58:09,14:58:09,20,331408,59856,cap-wifi5,-59,103200000,229400000,2800,2960,01:05:40,00:00:00
|
||||||
|
1788526691,14:58:11,14:58:11,10,322448,65576,cap-wifi5,-58,103200000,206500000,2800,2960,01:05:42,00:00:00
|
||||||
|
1788526692,14:58:12,RECONNECT
|
||||||
|
1
docs/messdaten/tp25-runde-1er/sampler-s22.out
Normal file
1
docs/messdaten/tp25-runde-1er/sampler-s22.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-1er/sampler-s22.csv (Takt 2s, Client 2A:0D:B0:5D:AD:A7)
|
||||||
1
docs/messdaten/tp25-runde-1er/sampler-s22.stderr.log
Normal file
1
docs/messdaten/tp25-runde-1er/sampler-s22.stderr.log
Normal file
@@ -0,0 +1 @@
|
|||||||
|
14:58:12 Verbindung weg -- neuer Versuch in 2 s
|
||||||
242
docs/messdaten/tp25-runde-1er/walk-ping-iphone.csv
Normal file
242
docs/messdaten/tp25-runde-1er/walk-ping-iphone.csv
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
ts_local,seq,ms
|
||||||
|
14:57:24,0,3ms102us
|
||||||
|
14:57:24,1,2ms682us
|
||||||
|
14:57:25,2,2ms739us
|
||||||
|
14:57:25,3,2ms889us
|
||||||
|
14:57:25,4,2ms955us
|
||||||
|
14:57:25,5,4ms434us
|
||||||
|
14:57:25,6,4ms59us
|
||||||
|
14:57:26,7,3ms540us
|
||||||
|
14:57:26,8,98ms220us
|
||||||
|
14:57:26,9,2ms841us
|
||||||
|
14:57:26,10,95ms173us
|
||||||
|
14:57:26,11,2ms688us
|
||||||
|
14:57:27,12,113ms888us
|
||||||
|
14:57:27,13,4ms866us
|
||||||
|
14:57:27,14,2ms909us
|
||||||
|
14:57:27,15,2ms776us
|
||||||
|
14:57:27,16,2ms572us
|
||||||
|
14:57:28,17,2ms931us
|
||||||
|
14:57:28,18,2ms663us
|
||||||
|
14:57:28,19,2ms796us
|
||||||
|
14:57:28,20,2ms736us
|
||||||
|
14:57:28,21,2ms826us
|
||||||
|
14:57:29,22,2ms778us
|
||||||
|
14:57:29,23,2ms741us
|
||||||
|
14:57:29,24,2ms904us
|
||||||
|
14:57:29,25,2ms744us
|
||||||
|
14:57:29,26,2ms845us
|
||||||
|
14:57:30,27,2ms850us
|
||||||
|
14:57:30,28,2ms746us
|
||||||
|
14:57:30,29,2ms973us
|
||||||
|
14:57:30,30,2ms907us
|
||||||
|
14:57:30,31,2ms771us
|
||||||
|
14:57:31,32,2ms778us
|
||||||
|
14:57:31,33,2ms793us
|
||||||
|
14:57:31,34,2ms823us
|
||||||
|
14:57:31,35,2ms717us
|
||||||
|
14:57:31,36,5ms311us
|
||||||
|
14:57:32,37,2ms752us
|
||||||
|
14:57:32,38,2ms799us
|
||||||
|
14:57:32,39,2ms964us
|
||||||
|
14:57:32,40,2ms760us
|
||||||
|
14:57:32,41,3ms53us
|
||||||
|
14:57:33,42,2ms775us
|
||||||
|
14:57:33,43,2ms793us
|
||||||
|
14:57:33,44,4ms125us
|
||||||
|
14:57:33,45,2ms696us
|
||||||
|
14:57:33,46,2ms686us
|
||||||
|
14:57:34,47,2ms738us
|
||||||
|
14:57:34,48,2ms824us
|
||||||
|
14:57:34,49,2ms607us
|
||||||
|
14:57:34,50,2ms944us
|
||||||
|
14:57:34,51,2ms854us
|
||||||
|
14:57:35,52,2ms724us
|
||||||
|
14:57:35,53,3ms453us
|
||||||
|
14:57:35,54,3ms417us
|
||||||
|
14:57:35,55,3ms535us
|
||||||
|
14:57:35,56,2ms719us
|
||||||
|
14:57:36,57,4ms498us
|
||||||
|
14:57:36,58,2ms706us
|
||||||
|
14:57:36,59,4ms279us
|
||||||
|
14:57:36,60,2ms836us
|
||||||
|
14:57:36,61,2ms646us
|
||||||
|
14:57:37,62,2ms873us
|
||||||
|
14:57:37,63,2ms782us
|
||||||
|
14:57:37,64,2ms624us
|
||||||
|
14:57:37,65,2ms881us
|
||||||
|
14:57:37,66,2ms716us
|
||||||
|
14:57:38,67,2ms763us
|
||||||
|
14:57:38,68,2ms833us
|
||||||
|
14:57:38,69,2ms879us
|
||||||
|
14:57:38,70,2ms851us
|
||||||
|
14:57:38,71,2ms777us
|
||||||
|
14:57:39,72,2ms937us
|
||||||
|
14:57:39,73,2ms821us
|
||||||
|
14:57:39,74,2ms729us
|
||||||
|
14:57:39,75,2ms892us
|
||||||
|
14:57:39,76,2ms729us
|
||||||
|
14:57:40,77,2ms755us
|
||||||
|
14:57:40,78,2ms932us
|
||||||
|
14:57:40,79,2ms701us
|
||||||
|
14:57:40,80,2ms662us
|
||||||
|
14:57:40,81,2ms651us
|
||||||
|
14:57:41,82,5ms991us
|
||||||
|
14:57:41,83,2ms728us
|
||||||
|
14:57:41,84,2ms756us
|
||||||
|
14:57:41,85,2ms716us
|
||||||
|
14:57:41,86,2ms707us
|
||||||
|
14:57:42,87,2ms785us
|
||||||
|
14:57:42,88,2ms594us
|
||||||
|
14:57:42,89,2ms747us
|
||||||
|
14:57:42,90,2ms712us
|
||||||
|
14:57:42,91,3ms258us
|
||||||
|
14:57:43,92,2ms727us
|
||||||
|
14:57:43,93,2ms623us
|
||||||
|
14:57:43,94,2ms723us
|
||||||
|
14:57:43,95,2ms749us
|
||||||
|
14:57:43,96,2ms700us
|
||||||
|
14:57:44,97,2ms723us
|
||||||
|
14:57:44,98,2ms735us
|
||||||
|
14:57:44,99,3ms646us
|
||||||
|
14:57:44,100,2ms718us
|
||||||
|
14:57:44,101,2ms740us
|
||||||
|
14:57:45,102,2ms768us
|
||||||
|
14:57:45,103,2ms663us
|
||||||
|
14:57:45,104,2ms639us
|
||||||
|
14:57:45,105,2ms731us
|
||||||
|
14:57:45,106,3ms290us
|
||||||
|
14:57:46,107,2ms822us
|
||||||
|
14:57:46,108,2ms844us
|
||||||
|
14:57:46,109,2ms617us
|
||||||
|
14:57:46,110,2ms734us
|
||||||
|
14:57:46,111,3ms447us
|
||||||
|
14:57:47,112,3ms169us
|
||||||
|
14:57:47,113,3ms247us
|
||||||
|
14:57:47,114,2ms564us
|
||||||
|
14:57:47,115,4ms516us
|
||||||
|
14:57:47,116,2ms623us
|
||||||
|
14:57:48,117,2ms675us
|
||||||
|
14:57:48,118,2ms877us
|
||||||
|
14:57:48,119,3ms310us
|
||||||
|
14:57:48,120,2ms851us
|
||||||
|
14:57:48,121,2ms803us
|
||||||
|
14:57:49,122,7ms998us
|
||||||
|
14:57:49,123,2ms887us
|
||||||
|
14:57:49,124,2ms852us
|
||||||
|
14:57:49,125,2ms846us
|
||||||
|
14:57:49,126,2ms899us
|
||||||
|
14:57:50,127,2ms913us
|
||||||
|
14:57:50,128,2ms680us
|
||||||
|
14:57:50,129,2ms715us
|
||||||
|
14:57:50,130,3ms374us
|
||||||
|
14:57:50,131,2ms670us
|
||||||
|
14:57:51,132,3ms148us
|
||||||
|
14:57:51,133,2ms932us
|
||||||
|
14:57:51,134,2ms831us
|
||||||
|
14:57:51,135,4ms780us
|
||||||
|
14:57:51,136,2ms911us
|
||||||
|
14:57:52,137,3ms85us
|
||||||
|
14:57:52,138,3ms589us
|
||||||
|
14:57:52,139,2ms697us
|
||||||
|
14:57:52,140,4ms876us
|
||||||
|
14:57:52,141,2ms867us
|
||||||
|
14:57:53,142,3ms283us
|
||||||
|
14:57:53,143,2ms710us
|
||||||
|
14:57:53,144,2ms801us
|
||||||
|
14:57:53,145,2ms928us
|
||||||
|
14:57:53,146,2ms640us
|
||||||
|
14:57:54,147,2ms678us
|
||||||
|
14:57:54,148,4ms976us
|
||||||
|
14:57:54,149,3ms408us
|
||||||
|
14:57:54,150,2ms706us
|
||||||
|
14:57:54,151,2ms827us
|
||||||
|
14:57:55,152,2ms884us
|
||||||
|
14:57:55,153,2ms855us
|
||||||
|
14:57:55,154,2ms787us
|
||||||
|
14:57:55,155,2ms695us
|
||||||
|
14:57:55,156,2ms902us
|
||||||
|
14:57:56,157,2ms749us
|
||||||
|
14:57:56,158,2ms838us
|
||||||
|
14:57:56,159,3ms937us
|
||||||
|
14:57:57,160,3ms43us
|
||||||
|
14:57:57,161,2ms870us
|
||||||
|
14:57:57,162,3ms357us
|
||||||
|
14:57:57,163,2ms756us
|
||||||
|
14:57:57,164,2ms679us
|
||||||
|
14:57:57,165,2ms863us
|
||||||
|
14:57:57,166,2ms768us
|
||||||
|
14:57:58,167,2ms858us
|
||||||
|
14:57:58,168,3ms30us
|
||||||
|
14:57:58,169,2ms677us
|
||||||
|
14:57:58,170,2ms725us
|
||||||
|
14:57:58,171,2ms919us
|
||||||
|
14:57:59,172,2ms688us
|
||||||
|
14:57:59,173,2ms821us
|
||||||
|
14:57:59,174,2ms805us
|
||||||
|
14:57:59,175,2ms709us
|
||||||
|
14:57:59,176,2ms989us
|
||||||
|
14:58:00,177,2ms589us
|
||||||
|
14:58:00,178,3ms835us
|
||||||
|
14:58:00,179,3ms194us
|
||||||
|
14:58:00,180,2ms662us
|
||||||
|
14:58:00,181,2ms686us
|
||||||
|
14:58:01,182,2ms704us
|
||||||
|
14:58:01,183,2ms752us
|
||||||
|
14:58:01,184,2ms656us
|
||||||
|
14:58:01,185,2ms904us
|
||||||
|
14:58:01,186,2ms823us
|
||||||
|
14:58:02,187,2ms656us
|
||||||
|
14:58:02,188,2ms813us
|
||||||
|
14:58:02,189,2ms644us
|
||||||
|
14:58:02,190,2ms782us
|
||||||
|
14:58:02,191,2ms722us
|
||||||
|
14:58:03,192,2ms674us
|
||||||
|
14:58:03,193,5ms657us
|
||||||
|
14:58:03,194,4ms701us
|
||||||
|
14:58:03,195,2ms772us
|
||||||
|
14:58:03,196,2ms661us
|
||||||
|
14:58:04,197,2ms660us
|
||||||
|
14:58:04,198,2ms859us
|
||||||
|
14:58:04,199,2ms800us
|
||||||
|
14:58:04,200,2ms706us
|
||||||
|
14:58:04,201,2ms847us
|
||||||
|
14:58:05,202,2ms625us
|
||||||
|
14:58:05,203,2ms816us
|
||||||
|
14:58:05,204,2ms703us
|
||||||
|
14:58:05,205,2ms818us
|
||||||
|
14:58:05,206,3ms162us
|
||||||
|
14:58:06,207,2ms673us
|
||||||
|
14:58:06,208,2ms781us
|
||||||
|
14:58:06,209,2ms607us
|
||||||
|
14:58:06,210,2ms990us
|
||||||
|
14:58:06,211,2ms602us
|
||||||
|
14:58:07,212,2ms711us
|
||||||
|
14:58:07,213,2ms819us
|
||||||
|
14:58:07,214,2ms757us
|
||||||
|
14:58:07,215,2ms605us
|
||||||
|
14:58:07,216,2ms770us
|
||||||
|
14:58:08,217,2ms682us
|
||||||
|
14:58:08,218,4ms824us
|
||||||
|
14:58:08,219,2ms623us
|
||||||
|
14:58:08,220,2ms566us
|
||||||
|
14:58:08,221,2ms724us
|
||||||
|
14:58:09,222,3ms737us
|
||||||
|
14:58:09,223,3ms273us
|
||||||
|
14:58:09,224,2ms809us
|
||||||
|
14:58:09,225,2ms619us
|
||||||
|
14:58:09,226,2ms709us
|
||||||
|
14:58:10,227,2ms909us
|
||||||
|
14:58:10,228,2ms707us
|
||||||
|
14:58:10,229,2ms909us
|
||||||
|
14:58:10,230,2ms610us
|
||||||
|
14:58:10,231,3ms251us
|
||||||
|
14:58:11,232,2ms689us
|
||||||
|
14:58:11,233,2ms714us
|
||||||
|
14:58:11,234,2ms680us
|
||||||
|
14:58:11,235,2ms644us
|
||||||
|
14:58:11,236,2ms665us
|
||||||
|
14:58:12,237,3ms326us
|
||||||
|
14:58:12,238,2ms749us
|
||||||
|
14:58:12,239,2ms713us
|
||||||
|
14:58:12,,RECONNECT
|
||||||
|
238
docs/messdaten/tp25-runde-1er/walk-ping-s22.csv
Normal file
238
docs/messdaten/tp25-runde-1er/walk-ping-s22.csv
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
ts_local,seq,ms
|
||||||
|
14:57:24,0,
|
||||||
|
14:57:25,1,
|
||||||
|
14:57:25,2,
|
||||||
|
14:57:25,3,
|
||||||
|
14:57:25,4,
|
||||||
|
14:57:25,5,
|
||||||
|
14:57:26,6,
|
||||||
|
14:57:26,7,
|
||||||
|
14:57:26,8,
|
||||||
|
14:57:26,9,
|
||||||
|
14:57:26,10,
|
||||||
|
14:57:27,11,
|
||||||
|
14:57:27,12,
|
||||||
|
14:57:27,13,
|
||||||
|
14:57:27,14,
|
||||||
|
14:57:27,15,unre...
|
||||||
|
14:57:28,16,
|
||||||
|
14:57:28,17,
|
||||||
|
14:57:28,18,
|
||||||
|
14:57:28,19,
|
||||||
|
14:57:28,20,
|
||||||
|
14:57:29,21,
|
||||||
|
14:57:29,22,
|
||||||
|
14:57:29,23,
|
||||||
|
14:57:29,24,
|
||||||
|
14:57:29,25,
|
||||||
|
14:57:30,26,
|
||||||
|
14:57:30,27,
|
||||||
|
14:57:30,28,
|
||||||
|
14:57:30,29,
|
||||||
|
14:57:30,30,
|
||||||
|
14:57:31,31,unre...
|
||||||
|
14:57:31,32,
|
||||||
|
14:57:31,33,
|
||||||
|
14:57:31,34,
|
||||||
|
14:57:31,35,
|
||||||
|
14:57:32,36,
|
||||||
|
14:57:32,37,
|
||||||
|
14:57:32,38,
|
||||||
|
14:57:32,39,
|
||||||
|
14:57:32,40,
|
||||||
|
14:57:33,41,
|
||||||
|
14:57:33,42,
|
||||||
|
14:57:33,43,
|
||||||
|
14:57:33,44,
|
||||||
|
14:57:33,45,
|
||||||
|
14:57:34,46,
|
||||||
|
14:57:34,47,unre...
|
||||||
|
14:57:34,48,
|
||||||
|
14:57:34,49,
|
||||||
|
14:57:34,50,
|
||||||
|
14:57:35,51,
|
||||||
|
14:57:35,52,
|
||||||
|
14:57:35,53,
|
||||||
|
14:57:35,54,
|
||||||
|
14:57:35,55,
|
||||||
|
14:57:36,56,
|
||||||
|
14:57:36,57,
|
||||||
|
14:57:36,58,
|
||||||
|
14:57:36,59,
|
||||||
|
14:57:37,60,
|
||||||
|
14:57:37,61,
|
||||||
|
14:57:37,62,
|
||||||
|
14:57:37,63,unre...
|
||||||
|
14:57:38,64,
|
||||||
|
14:57:38,65,
|
||||||
|
14:57:38,66,
|
||||||
|
14:57:38,67,
|
||||||
|
14:57:38,68,
|
||||||
|
14:57:38,69,
|
||||||
|
14:57:39,70,
|
||||||
|
14:57:39,71,
|
||||||
|
14:57:39,72,
|
||||||
|
14:57:39,73,
|
||||||
|
14:57:39,74,
|
||||||
|
14:57:40,75,
|
||||||
|
14:57:40,76,
|
||||||
|
14:57:40,77,
|
||||||
|
14:57:40,78,
|
||||||
|
14:57:40,79,unre...
|
||||||
|
14:57:41,80,
|
||||||
|
14:57:41,81,
|
||||||
|
14:57:41,82,
|
||||||
|
14:57:41,83,
|
||||||
|
14:57:41,84,
|
||||||
|
14:57:42,85,
|
||||||
|
14:57:42,86,
|
||||||
|
14:57:42,87,
|
||||||
|
14:57:42,88,
|
||||||
|
14:57:42,89,
|
||||||
|
14:57:43,90,
|
||||||
|
14:57:43,91,
|
||||||
|
14:57:43,92,
|
||||||
|
14:57:43,93,
|
||||||
|
14:57:43,94,
|
||||||
|
14:57:43,95,unre...
|
||||||
|
14:57:44,96,
|
||||||
|
14:57:44,97,
|
||||||
|
14:57:44,98,
|
||||||
|
14:57:44,99,
|
||||||
|
14:57:45,100,
|
||||||
|
14:57:45,101,
|
||||||
|
14:57:45,102,
|
||||||
|
14:57:45,103,
|
||||||
|
14:57:45,104,
|
||||||
|
14:57:46,105,
|
||||||
|
14:57:46,106,
|
||||||
|
14:57:46,107,
|
||||||
|
14:57:46,108,
|
||||||
|
14:57:46,109,
|
||||||
|
14:57:47,110,
|
||||||
|
14:57:47,111,unre...
|
||||||
|
14:57:47,112,
|
||||||
|
14:57:47,113,
|
||||||
|
14:57:47,114,
|
||||||
|
14:57:48,115,
|
||||||
|
14:57:48,116,
|
||||||
|
14:57:48,117,
|
||||||
|
14:57:48,118,
|
||||||
|
14:57:49,119,
|
||||||
|
14:57:49,120,
|
||||||
|
14:57:49,121,
|
||||||
|
14:57:49,122,
|
||||||
|
14:57:49,123,
|
||||||
|
14:57:50,124,
|
||||||
|
14:57:50,125,
|
||||||
|
14:57:50,126,
|
||||||
|
14:57:50,127,unre...
|
||||||
|
14:57:50,128,
|
||||||
|
14:57:51,129,
|
||||||
|
14:57:51,130,
|
||||||
|
14:57:51,131,
|
||||||
|
14:57:51,132,
|
||||||
|
14:57:51,133,
|
||||||
|
14:57:52,134,
|
||||||
|
14:57:52,135,
|
||||||
|
14:57:52,136,
|
||||||
|
14:57:52,137,
|
||||||
|
14:57:52,138,
|
||||||
|
14:57:53,139,
|
||||||
|
14:57:53,140,
|
||||||
|
14:57:53,141,
|
||||||
|
14:57:53,142,
|
||||||
|
14:57:53,143,unre...
|
||||||
|
14:57:54,144,
|
||||||
|
14:57:54,145,
|
||||||
|
14:57:54,146,
|
||||||
|
14:57:54,147,
|
||||||
|
14:57:54,148,
|
||||||
|
14:57:55,149,
|
||||||
|
14:57:55,150,
|
||||||
|
14:57:55,151,
|
||||||
|
14:57:55,152,
|
||||||
|
14:57:55,153,
|
||||||
|
14:57:56,154,
|
||||||
|
14:57:56,155,
|
||||||
|
14:57:56,156,
|
||||||
|
14:57:57,157,
|
||||||
|
14:57:57,158,
|
||||||
|
14:57:57,159,unre...
|
||||||
|
14:57:57,160,
|
||||||
|
14:57:57,161,
|
||||||
|
14:57:57,162,
|
||||||
|
14:57:57,163,
|
||||||
|
14:57:58,164,
|
||||||
|
14:57:58,165,
|
||||||
|
14:57:58,166,
|
||||||
|
14:57:58,167,
|
||||||
|
14:57:58,168,
|
||||||
|
14:57:59,169,
|
||||||
|
14:57:59,170,
|
||||||
|
14:57:59,171,
|
||||||
|
14:57:59,172,
|
||||||
|
14:57:59,173,
|
||||||
|
14:58:00,174,
|
||||||
|
14:58:00,175,unre...
|
||||||
|
14:58:00,176,
|
||||||
|
14:58:00,177,
|
||||||
|
14:58:00,178,
|
||||||
|
14:58:01,179,
|
||||||
|
14:58:01,180,
|
||||||
|
14:58:01,181,
|
||||||
|
14:58:01,182,
|
||||||
|
14:58:01,183,
|
||||||
|
14:58:02,184,
|
||||||
|
14:58:02,185,
|
||||||
|
14:58:02,186,
|
||||||
|
14:58:02,187,
|
||||||
|
14:58:02,188,
|
||||||
|
14:58:03,189,
|
||||||
|
14:58:03,190,
|
||||||
|
14:58:03,191,unre...
|
||||||
|
14:58:03,192,
|
||||||
|
14:58:03,193,
|
||||||
|
14:58:04,194,
|
||||||
|
14:58:04,195,
|
||||||
|
14:58:04,196,
|
||||||
|
14:58:04,197,
|
||||||
|
14:58:04,198,
|
||||||
|
14:58:05,199,
|
||||||
|
14:58:05,200,
|
||||||
|
14:58:05,201,
|
||||||
|
14:58:05,202,
|
||||||
|
14:58:05,203,
|
||||||
|
14:58:06,204,
|
||||||
|
14:58:06,205,
|
||||||
|
14:58:06,206,
|
||||||
|
14:58:06,207,unre...
|
||||||
|
14:58:07,208,
|
||||||
|
14:58:07,209,
|
||||||
|
14:58:07,210,
|
||||||
|
14:58:07,211,
|
||||||
|
14:58:07,212,
|
||||||
|
14:58:08,213,
|
||||||
|
14:58:08,214,
|
||||||
|
14:58:08,215,
|
||||||
|
14:58:08,216,
|
||||||
|
14:58:08,217,
|
||||||
|
14:58:09,218,
|
||||||
|
14:58:09,219,
|
||||||
|
14:58:09,220,
|
||||||
|
14:58:09,221,
|
||||||
|
14:58:09,222,
|
||||||
|
14:58:09,223,unre...
|
||||||
|
14:58:10,224,
|
||||||
|
14:58:10,225,
|
||||||
|
14:58:10,226,
|
||||||
|
14:58:10,227,
|
||||||
|
14:58:11,228,
|
||||||
|
14:58:11,229,
|
||||||
|
14:58:11,230,
|
||||||
|
14:58:11,231,
|
||||||
|
14:58:11,232,
|
||||||
|
14:58:12,233,
|
||||||
|
14:58:12,234,
|
||||||
|
14:58:12,235,
|
||||||
|
14:58:12,,RECONNECT
|
||||||
|
6
docs/messdaten/tp25-runde-dauerping/ap-map.txt
Normal file
6
docs/messdaten/tp25-runde-dauerping/ap-map.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
cap-wifi1=ap2/5GHz
|
||||||
|
cap-wifi2=ap3/5GHz
|
||||||
|
cap-wifi3=ap1/5GHz
|
||||||
|
cap-wifi4=ap3/2,4GHz
|
||||||
|
cap-wifi5=ap1/2,4GHz
|
||||||
|
cap-wifi6=ap2/2,4GHz
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
ts_epoch,ts_local,marker
|
||||||
|
@@ -0,0 +1,43 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788526610,14:56:50,14:56:50,50,160216,1109704,cap-wifi2,-69,137600000,275300000,45848,2240,01:04:23,00:00:00
|
||||||
|
1788526612,14:56:52,14:56:52,6,151312,62480,cap-wifi2,-69,137600000,275300000,45848,2240,01:04:25,00:00:00
|
||||||
|
1788526614,14:56:54,14:56:54,15,127416,44832,cap-wifi2,-69,137600000,309700000,36152,6320,01:04:27,00:00:00
|
||||||
|
1788526616,14:56:56,14:56:56,4,127712,21960,cap-wifi2,-69,103200000,325000000,42008,9736,01:04:29,00:00:00
|
||||||
|
1788526618,14:56:58,14:56:58,9,157888,29816,cap-wifi2,-69,103200000,325000000,42008,9736,01:04:31,00:00:00
|
||||||
|
1788526620,14:57:00,14:57:00,16,322208,124400,cap-wifi2,-69,103200000,344100000,123600,58608,01:04:33,00:00:00
|
||||||
|
1788526622,14:57:02,14:57:03,10,451416,4332552,cap-wifi2,-69,137600000,275300000,193568,29704,01:04:35,00:00:00
|
||||||
|
1788526624,14:57:04,14:57:05,9,398720,136640,cap-wifi2,-69,137600000,292500000,40792,7440,01:04:37,00:00:00
|
||||||
|
1788526627,14:57:07,14:57:07,15,947800,446984,cap-wifi2,-69,137600000,292500000,40792,7440,01:04:40,00:00:00
|
||||||
|
1788526629,14:57:09,14:57:09,12,7610296,3321592,cap-wifi2,-69,154900000,292500000,89400,14304,01:04:42,00:00:00
|
||||||
|
1788526631,14:57:11,14:57:11,18,2720136,2091232,cap-wifi2,-69,103200000,275300000,69264,10760,01:04:44,00:00:00
|
||||||
|
1788526633,14:57:13,14:57:13,9,2261568,2680088,cap-wifi2,-69,103200000,275300000,69264,10760,01:04:46,00:00:00
|
||||||
|
1788526635,14:57:15,14:57:15,7,108584,42016,cap-wifi2,-69,103200000,309700000,78112,11920,01:04:48,00:00:00
|
||||||
|
1788526637,14:57:17,14:57:17,4,309312,51096,cap-wifi2,-68,103200000,309700000,117392,10152,01:04:50,00:00:00
|
||||||
|
1788526639,14:57:19,14:57:19,14,309864,66576,cap-wifi2,-68,103200000,309700000,117392,10152,01:04:52,00:00:00
|
||||||
|
1788526641,14:57:21,14:57:21,6,150056,22800,cap-wifi2,-68,103200000,309700000,53888,10272,01:04:54,00:00:00
|
||||||
|
1788526643,14:57:23,14:57:23,14,510016,213944,cap-wifi2,-68,103200000,309700000,61328,9088,01:04:56,00:00:00
|
||||||
|
1788526645,14:57:25,14:57:25,19,507656,169768,cap-wifi2,-68,103200000,309700000,61328,9088,01:04:58,00:00:00
|
||||||
|
1788526647,14:57:27,14:57:27,9,136752,33856,cap-wifi2,-69,103200000,309700000,41640,8560,01:05:00,00:00:00
|
||||||
|
1788526649,14:57:29,14:57:29,18,143088,22488,cap-wifi2,-69,103200000,292500000,61120,8192,01:05:02,00:00:00
|
||||||
|
1788526651,14:57:31,14:57:31,15,192288,49440,cap-wifi2,-69,103200000,292500000,61120,8192,01:05:04,00:00:00
|
||||||
|
1788526653,14:57:33,14:57:33,9,131304,37448,cap-wifi2,-69,137600000,309700000,57760,8136,01:05:06,00:00:00
|
||||||
|
1788526655,14:57:35,14:57:35,11,92000,18872,cap-wifi2,-69,137600000,344100000,56408,8592,01:05:08,00:00:00
|
||||||
|
1788526657,14:57:37,14:57:37,9,130144,24840,cap-wifi2,-69,137600000,344100000,56408,8592,01:05:10,00:00:00
|
||||||
|
1788526659,14:57:39,14:57:39,20,105160,20800,cap-wifi2,-69,137600000,325000000,19344,5200,01:05:12,00:00:00
|
||||||
|
1788526661,14:57:41,14:57:41,11,177144,59112,cap-wifi2,-71,154900000,275300000,111184,14496,01:05:14,00:00:00
|
||||||
|
1788526663,14:57:43,14:57:43,16,136152,39360,cap-wifi2,-71,154900000,275300000,111184,14496,01:05:16,00:00:00
|
||||||
|
1788526665,14:57:45,14:57:45,16,132416,46304,cap-wifi2,-70,154900000,309700000,44928,8560,01:05:18,00:00:00
|
||||||
|
1788526667,14:57:47,14:57:47,11,462016,41704,cap-wifi2,-70,154900000,309700000,397536,18704,01:05:20,00:00:00
|
||||||
|
1788526669,14:57:49,14:57:49,24,129736,41416,cap-wifi2,-70,154900000,309700000,397536,18704,01:05:22,00:00:00
|
||||||
|
1788526671,14:57:51,14:57:51,7,401784,54464,cap-wifi2,-76,103200000,206500000,44784,8560,01:05:24,00:00:00
|
||||||
|
1788526673,14:57:53,14:57:53,14,101992,21720,cap-wifi2,-69,103200000,292500000,55400,9120,01:05:26,00:00:00
|
||||||
|
1788526675,14:57:55,14:57:55,14,152504,61272,cap-wifi2,-69,103200000,292500000,55400,9120,01:05:28,00:00:00
|
||||||
|
1788526677,14:57:57,14:57:57,22,130520,26552,cap-wifi2,-70,103200000,260000000,55304,10816,01:05:30,00:00:00
|
||||||
|
1788526679,14:57:59,14:57:59,17,155408,24688,cap-wifi2,-69,103200000,275300000,58480,8720,01:05:32,00:00:00
|
||||||
|
1788526681,14:58:01,14:58:01,9,244456,41424,cap-wifi2,-69,103200000,275300000,58480,8720,01:05:34,00:00:00
|
||||||
|
1788526683,14:58:03,14:58:03,10,115640,26032,cap-wifi2,-69,103200000,260000000,38208,22096,01:05:36,00:00:00
|
||||||
|
1788526685,14:58:05,14:58:05,10,154824,24456,cap-wifi2,-70,137600000,260000000,65000,8000,01:05:38,00:00:00
|
||||||
|
1788526687,14:58:07,14:58:07,9,135144,25000,cap-wifi2,-70,137600000,260000000,65000,8000,01:05:40,00:00:00
|
||||||
|
1788526689,14:58:09,14:58:10,20,122800,29936,cap-wifi2,-68,137600000,275300000,18856,4048,01:05:42,00:00:00
|
||||||
|
1788526691,14:58:11,14:58:12,10,152160,65768,cap-wifi2,-68,137600000,206500000,40064,12024,01:05:44,00:00:00
|
||||||
|
1788526692,14:58:12,RECONNECT
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-dauerping/sampler-ipad.csv (Takt 2s, Client CA:B8:D4:3F:8A:81)
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788526610,14:56:50,14:56:50,50,160216,1109704,cap-wifi3,-58,309700000,412900000,0,0,00:04:03,00:00:03.010
|
||||||
|
1788526612,14:56:52,14:56:52,6,151312,62480,cap-wifi3,-57,309700000,412900000,0,0,00:04:05,00:00:00
|
||||||
|
1788526614,14:56:54,14:56:54,15,127416,44832,cap-wifi3,-57,309700000,412900000,2800,2960,00:04:07,00:00:00
|
||||||
|
1788526616,14:56:56,14:56:56,4,127712,21960,cap-wifi3,-57,309700000,412900000,2800,2960,00:04:09,00:00:00
|
||||||
|
1788526618,14:56:58,14:56:58,9,157888,29816,cap-wifi3,-57,309700000,412900000,3248,3552,00:04:11,00:00:00
|
||||||
|
1788526620,14:57:00,14:57:00,16,322208,124400,cap-wifi3,-57,344100000,325000000,3360,3552,00:04:13,00:00:00
|
||||||
|
1788526622,14:57:02,14:57:03,10,451416,4332552,cap-wifi3,-58,344100000,344100000,2800,2960,00:04:15,00:00:00
|
||||||
|
1788526624,14:57:04,14:57:05,9,398720,136640,cap-wifi3,-58,344100000,344100000,2800,2960,00:04:17,00:00:00
|
||||||
|
1788526627,14:57:07,14:57:07,15,947800,446984,cap-wifi3,-59,344100000,325000000,3360,3552,00:04:20,00:00:00
|
||||||
|
1788526629,14:57:09,14:57:09,12,7610296,3321592,cap-wifi3,-58,344100000,344100000,2800,2960,00:04:22,00:00:00
|
||||||
|
1788526631,14:57:11,14:57:11,18,2720136,2091232,cap-wifi3,-58,344100000,344100000,2800,2960,00:04:24,00:00:00
|
||||||
|
1788526633,14:57:13,14:57:13,9,2261568,2680088,cap-wifi3,-57,344100000,325000000,3360,3552,00:04:26,00:00:00
|
||||||
|
1788526635,14:57:15,14:57:15,7,108584,42016,cap-wifi3,-57,344100000,344100000,2800,2960,00:04:28,00:00:00
|
||||||
|
1788526637,14:57:17,14:57:17,4,309312,51096,cap-wifi3,-57,344100000,344100000,2800,2960,00:04:30,00:00:00
|
||||||
|
1788526639,14:57:19,14:57:19,14,309864,66576,cap-wifi3,-58,344100000,325000000,2800,2960,00:04:32,00:00:00
|
||||||
|
1788526641,14:57:21,14:57:21,6,150056,22800,cap-wifi3,-57,309700000,344100000,3808,4000,00:04:34,00:00:00
|
||||||
|
1788526643,14:57:23,14:57:23,14,510016,213944,cap-wifi3,-57,309700000,344100000,3808,4000,00:04:36,00:00:00
|
||||||
|
1788526645,14:57:25,14:57:25,19,507656,169768,cap-wifi3,-57,309700000,325000000,2800,2960,00:04:38,00:00:00
|
||||||
|
1788526647,14:57:27,14:57:27,9,136752,33856,cap-wifi3,-59,309700000,309700000,6160,6512,00:04:40,00:00:00
|
||||||
|
1788526649,14:57:29,14:57:29,18,143088,22488,cap-wifi3,-59,309700000,309700000,6160,6512,00:04:42,00:00:00
|
||||||
|
1788526651,14:57:31,14:57:31,15,192288,49440,cap-wifi3,-59,309700000,344100000,6160,6512,00:04:44,00:00:00
|
||||||
|
1788526653,14:57:33,14:57:33,9,131304,37448,cap-wifi3,-59,309700000,412900000,5600,5920,00:04:46,00:00:00
|
||||||
|
1788526655,14:57:35,14:57:35,11,92000,18872,cap-wifi3,-59,309700000,412900000,5600,5920,00:04:48,00:00:00
|
||||||
|
1788526657,14:57:37,14:57:37,9,130144,24840,cap-wifi3,-60,309700000,390000000,6048,6512,00:04:50,00:00:00
|
||||||
|
1788526659,14:57:39,14:57:39,20,105160,20800,cap-wifi3,-57,309700000,344100000,6048,6288,00:04:52,00:00:00
|
||||||
|
1788526661,14:57:41,14:57:41,11,177144,59112,cap-wifi3,-57,309700000,344100000,6048,6288,00:04:54,00:00:00
|
||||||
|
1788526663,14:57:43,14:57:43,16,136152,39360,cap-wifi3,-59,309700000,325000000,5600,5920,00:04:56,00:00:00
|
||||||
|
1788526665,14:57:45,14:57:45,16,132416,46304,cap-wifi3,-60,309700000,390000000,5600,6296,00:04:58,00:00:00
|
||||||
|
1788526667,14:57:47,14:57:47,11,462016,41704,cap-wifi3,-60,309700000,390000000,5600,6296,00:05:00,00:00:00
|
||||||
|
1788526669,14:57:49,14:57:49,24,129736,41416,cap-wifi3,-59,309700000,390000000,5600,5920,00:05:02,00:00:00
|
||||||
|
1788526671,14:57:51,14:57:51,7,234376,122480,cap-wifi3,-56,309700000,433300000,6640,7008,00:05:04,00:00:00
|
||||||
|
1788526673,14:57:53,14:57:53,14,101992,21720,cap-wifi3,-56,309700000,433300000,6640,7008,00:05:06,00:00:00
|
||||||
|
1788526675,14:57:55,14:57:55,14,152504,61272,cap-wifi3,-59,309700000,433300000,6160,6512,00:05:08,00:00:00
|
||||||
|
1788526677,14:57:57,14:57:57,22,130520,26552,cap-wifi3,-57,309700000,433300000,6160,6512,00:05:10,00:00:00
|
||||||
|
1788526679,14:57:59,14:57:59,17,155408,24688,cap-wifi3,-57,309700000,433300000,6160,6512,00:05:12,00:00:00
|
||||||
|
1788526681,14:58:01,14:58:01,9,244456,41424,cap-wifi3,-58,309700000,433300000,6160,6512,00:05:14,00:00:00
|
||||||
|
1788526683,14:58:03,14:58:03,10,115640,26032,cap-wifi3,-59,309700000,433300000,5600,5920,00:05:16,00:00:00
|
||||||
|
1788526685,14:58:05,14:58:05,10,154824,24456,cap-wifi3,-59,309700000,433300000,5600,5920,00:05:18,00:00:00
|
||||||
|
1788526687,14:58:07,14:58:08,9,135144,25000,cap-wifi3,-59,309700000,487500000,5600,5920,00:05:20,00:00:00
|
||||||
|
1788526689,14:58:09,14:58:10,20,122800,29936,cap-wifi3,-58,309700000,455800000,5600,5920,00:05:22,00:00:00
|
||||||
|
1788526691,14:58:11,14:58:12,10,152160,65768,cap-wifi3,-58,309700000,455800000,6160,6512,00:05:24,00:00:00
|
||||||
|
1788526692,14:58:12,RECONNECT
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-dauerping/sampler-iphone.csv (Takt 2s, Client F2:62:4C:E1:AF:09)
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788526611,14:56:51,14:56:51,38,120880,24032,cap-wifi6,-85,68800000,17200000,166280,4310536,01:04:25,00:00:00
|
||||||
|
1788526613,14:56:53,14:56:53,9,151312,62480,cap-wifi6,-85,103200000,17200000,550344,592824,01:04:27,00:00:00
|
||||||
|
1788526615,14:56:55,14:56:55,10,127416,44832,cap-wifi6,-86,68800000,25800000,413184,340240,01:04:29,00:00:00
|
||||||
|
1788526617,14:56:57,14:56:57,6,127712,21960,cap-wifi6,-86,68800000,25800000,413184,340240,01:04:31,00:00:00
|
||||||
|
1788526619,14:56:59,14:56:59,13,157888,29816,cap-wifi6,-85,103200000,34400000,1524128,1126592,01:04:33,00:00:00
|
||||||
|
1788526621,14:57:01,14:57:01,7,322208,124400,cap-wifi6,-86,68800000,34400000,1886112,547568,01:04:35,00:00:00
|
||||||
|
1788526623,14:57:03,14:57:03,13,451416,4332552,cap-wifi6,-86,68800000,34400000,1886112,547568,01:04:37,00:00:00
|
||||||
|
1788526625,14:57:05,14:57:05,10,398720,136640,cap-wifi6,-85,68800000,17200000,1079440,2625392,01:04:39,00:00:00
|
||||||
|
1788526627,14:57:07,14:57:07,12,947800,446984,cap-wifi6,-86,68800000,17200000,1108088,760040,01:04:42,00:00:00
|
||||||
|
1788526629,14:57:09,14:57:09,20,7610296,3321592,cap-wifi6,-86,68800000,17200000,1108088,760040,01:04:44,00:00:00
|
||||||
|
1788526631,14:57:11,14:57:11,8,2720136,2091232,cap-wifi6,-82,68800000,51600000,1684720,1013032,01:04:46,00:00:00
|
||||||
|
1788526633,14:57:13,14:57:13,9,2261568,2680088,cap-wifi6,-87,103200000,25800000,324776,3447064,01:04:48,00:00:00
|
||||||
|
1788526636,14:57:16,14:57:15,7,108584,42016,cap-wifi6,-87,103200000,25800000,324776,3447064,01:04:50,00:00:00
|
||||||
|
1788526637,14:57:17,14:57:17,4,1478952,581848,cap-wifi6,-85,103200000,17200000,598680,602896,01:04:52,00:00:00
|
||||||
|
1788526639,14:57:19,14:57:19,14,229272,54576,cap-wifi6,-84,103200000,34400000,1017664,775360,01:04:54,00:00:00
|
||||||
|
1788526641,14:57:21,14:57:21,6,100688,29040,cap-wifi6,-84,103200000,34400000,1017664,775360,01:04:56,00:00:00
|
||||||
|
1788526644,14:57:24,14:57:23,14,472232,195392,cap-wifi6,-86,68800000,17200000,14752,6688,01:04:58,00:00:00
|
||||||
|
1788526645,14:57:25,14:57:25,19,229864,36304,cap-wifi6,-86,68800000,17200000,1094984,789880,01:05:00,00:00:00
|
||||||
|
1788526647,14:57:27,14:57:28,9,132384,26968,cap-wifi6,-86,68800000,17200000,1094984,789880,01:05:02,00:00:00
|
||||||
|
1788526649,14:57:29,14:57:30,18,196672,48240,cap-wifi6,-84,68800000,51600000,912296,563704,01:05:04,00:00:00
|
||||||
|
1788526652,14:57:32,14:57:32,15,237176,31328,cap-wifi6,-87,68800000,11000000,2448600,1495240,01:05:06,00:00:00
|
||||||
|
1788526654,14:57:34,14:57:34,9,147328,27456,cap-wifi6,-87,68800000,17200000,1326824,729536,01:05:08,00:00:00
|
||||||
|
1788526656,14:57:36,14:57:36,11,122784,22816,cap-wifi6,-87,68800000,17200000,1326824,729536,01:05:10,00:00:00
|
||||||
|
1788526658,14:57:38,14:57:38,9,122248,26192,cap-wifi6,-83,68800000,51600000,1292552,729408,01:05:12,00:00:00
|
||||||
|
1788526660,14:57:40,14:57:40,14,167632,49032,cap-wifi6,-87,103200000,17200000,2131488,1138888,01:05:14,00:00:00
|
||||||
|
1788526662,14:57:42,14:57:42,11,89248,17856,cap-wifi6,-87,103200000,17200000,2131488,1138888,01:05:16,00:00:00
|
||||||
|
1788526664,14:57:44,14:57:44,7,125232,33536,cap-wifi6,-86,103200000,17200000,1495112,774112,01:05:18,00:00:00
|
||||||
|
1788526666,14:57:46,14:57:46,8,1056872,260048,cap-wifi6,-86,103200000,17200000,773656,584032,01:05:20,00:00:00
|
||||||
|
1788526668,14:57:48,14:57:48,11,172776,252208,cap-wifi6,-86,103200000,17200000,773656,584032,01:05:22,00:00:00
|
||||||
|
1788526670,14:57:50,14:57:50,14,116656,22744,cap-wifi6,-87,51600000,17200000,256424,250360,01:05:24,00:00:00
|
||||||
|
1788526672,14:57:52,14:57:52,8,234376,122480,cap-wifi6,-83,51600000,51600000,771864,575120,01:05:26,00:00:00
|
||||||
|
1788526674,14:57:54,14:57:54,14,101992,21720,cap-wifi6,-83,51600000,51600000,771864,575120,01:05:28,00:00:00
|
||||||
|
1788526676,14:57:56,14:57:56,9,152504,61272,cap-wifi6,-84,68800000,17200000,944720,547864,01:05:31,00:00:00
|
||||||
|
1788526678,14:57:58,14:57:58,15,130520,26552,cap-wifi6,-88,51600000,17200000,1086104,614336,01:05:33,00:00:00
|
||||||
|
1788526680,14:58:00,14:58:00,16,155408,24688,cap-wifi6,-88,51600000,17200000,1086104,614336,01:05:35,00:00:00
|
||||||
|
1788526682,14:58:02,14:58:02,10,244456,41424,cap-wifi6,-85,68800000,17200000,1515776,563200,01:05:37,00:00:00
|
||||||
|
1788526684,14:58:04,14:58:04,8,183848,56872,cap-wifi6,-84,103200000,25800000,888888,696272,01:05:39,00:00:00
|
||||||
|
1788526686,14:58:06,14:58:06,9,305320,47576,cap-wifi6,-84,103200000,25800000,888888,696272,01:05:41,00:00:00
|
||||||
|
1788526688,14:58:08,14:58:08,14,331408,59856,cap-wifi6,-84,68800000,34400000,860480,583048,01:05:43,00:00:00
|
||||||
|
1788526690,14:58:10,14:58:10,17,322448,65576,cap-wifi6,-87,51600000,25800000,1068352,553088,01:05:45,00:00:00
|
||||||
|
1788526692,14:58:12,RECONNECT
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-dauerping/sampler-macbook.csv (Takt 2s, Client 56:71:6C:22:E6:A9)
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788526611,14:56:51,14:56:51,38,120880,24032,cap-wifi5,-51,137600000,229400000,0,0,01:04:22,00:00:09
|
||||||
|
1788526613,14:56:53,14:56:53,9,151312,62480,cap-wifi5,-51,137600000,258100000,1680,1776,01:04:24,00:00:00
|
||||||
|
1788526615,14:56:55,14:56:55,10,127416,44832,cap-wifi5,-53,137600000,229400000,2800,2960,01:04:26,00:00:00
|
||||||
|
1788526617,14:56:57,14:56:57,6,127712,21960,cap-wifi5,-53,137600000,229400000,2800,2960,01:04:28,00:00:00
|
||||||
|
1788526619,14:56:59,14:56:59,13,157888,29816,cap-wifi5,-50,137600000,229400000,2800,2960,01:04:30,00:00:00
|
||||||
|
1788526621,14:57:01,14:57:01,7,322208,124400,cap-wifi5,-51,137600000,229400000,3248,3328,01:04:32,00:00:00
|
||||||
|
1788526623,14:57:03,14:57:03,13,451416,4332552,cap-wifi5,-51,137600000,229400000,3248,3328,01:04:34,00:00:00
|
||||||
|
1788526625,14:57:05,14:57:05,10,398720,136640,cap-wifi5,-51,137600000,229400000,2800,2960,01:04:36,00:00:00
|
||||||
|
1788526627,14:57:07,14:57:07,12,947800,446984,cap-wifi5,-51,137600000,229400000,2800,2960,01:04:38,00:00:00
|
||||||
|
1788526629,14:57:09,14:57:09,20,7610296,3321592,cap-wifi5,-51,137600000,229400000,2800,2960,01:04:40,00:00:00
|
||||||
|
1788526631,14:57:11,14:57:11,8,2720136,2091232,cap-wifi5,-52,137600000,229400000,3360,3552,01:04:42,00:00:00
|
||||||
|
1788526633,14:57:13,14:57:13,9,2261568,2680088,cap-wifi5,-52,137600000,229400000,3360,3552,01:04:44,00:00:00
|
||||||
|
1788526636,14:57:16,14:57:15,7,108584,42016,cap-wifi5,-52,137600000,229400000,3360,3552,01:04:46,00:00:00
|
||||||
|
1788526637,14:57:17,14:57:17,4,1478952,581848,cap-wifi5,-53,137600000,258100000,2800,2960,01:04:48,00:00:00
|
||||||
|
1788526639,14:57:19,14:57:19,14,229272,54576,cap-wifi5,-53,137600000,229400000,4184,4968,01:04:50,00:00:00
|
||||||
|
1788526641,14:57:21,14:57:21,6,100688,29040,cap-wifi5,-53,137600000,229400000,4184,4968,01:04:52,00:00:00
|
||||||
|
1788526644,14:57:24,14:57:23,14,472232,195392,cap-wifi5,-61,137600000,229400000,2800,2960,01:04:54,00:00:00
|
||||||
|
1788526645,14:57:25,14:57:25,19,229864,36304,cap-wifi5,-59,172100000,229400000,2800,2960,01:04:56,00:00:00
|
||||||
|
1788526647,14:57:27,14:57:28,9,132384,26968,cap-wifi5,-59,172100000,229400000,2800,2960,01:04:58,00:00:00
|
||||||
|
1788526649,14:57:29,14:57:30,18,196672,48240,cap-wifi5,-60,172100000,229400000,2800,2368,01:05:00,00:00:00
|
||||||
|
1788526652,14:57:32,14:57:32,15,237176,31328,cap-wifi5,-60,172100000,229400000,3360,3552,01:05:02,00:00:00
|
||||||
|
1788526654,14:57:34,14:57:34,9,147328,27456,cap-wifi5,-60,172100000,229400000,2800,2960,01:05:04,00:00:00
|
||||||
|
1788526656,14:57:36,14:57:36,11,122784,22816,cap-wifi5,-60,172100000,229400000,2800,2960,01:05:07,00:00:00
|
||||||
|
1788526658,14:57:38,14:57:38,9,122248,26192,cap-wifi5,-59,129000000,258100000,3856,2960,01:05:09,00:00:00
|
||||||
|
1788526660,14:57:40,14:57:40,14,167632,49032,cap-wifi5,-58,114700000,229400000,7888,15664,01:05:11,00:00:00
|
||||||
|
1788526662,14:57:42,14:57:42,11,89248,17856,cap-wifi5,-58,114700000,229400000,7888,15664,01:05:13,00:00:00
|
||||||
|
1788526664,14:57:44,14:57:44,7,125232,33536,cap-wifi5,-57,114700000,206500000,3360,2960,01:05:15,00:00:00
|
||||||
|
1788526666,14:57:46,14:57:46,8,1056872,260048,cap-wifi5,-57,114700000,206500000,2800,2960,01:05:17,00:00:00
|
||||||
|
1788526668,14:57:48,14:57:48,11,172776,252208,cap-wifi5,-57,114700000,206500000,2800,2960,01:05:19,00:00:00
|
||||||
|
1788526670,14:57:50,14:57:50,14,116656,22744,cap-wifi5,-57,154900000,172100000,3360,3552,01:05:21,00:00:00
|
||||||
|
1788526672,14:57:52,14:57:52,8,234376,122480,cap-wifi5,-58,154900000,206500000,2800,2960,01:05:23,00:00:00
|
||||||
|
1788526674,14:57:54,14:57:54,14,101992,21720,cap-wifi5,-58,154900000,206500000,2800,2960,01:05:25,00:00:00
|
||||||
|
1788526676,14:57:56,14:57:56,9,152504,61272,cap-wifi5,-58,154900000,229400000,2800,2960,01:05:27,00:00:00
|
||||||
|
1788526678,14:57:58,14:57:58,15,130520,26552,cap-wifi5,-58,103200000,229400000,2800,2960,01:05:29,00:00:00
|
||||||
|
1788526680,14:58:00,14:58:00,16,155408,24688,cap-wifi5,-58,103200000,229400000,2800,2960,01:05:31,00:00:00
|
||||||
|
1788526682,14:58:02,14:58:02,10,244456,41424,cap-wifi5,-58,103200000,206500000,3248,3328,01:05:33,00:00:00
|
||||||
|
1788526684,14:58:04,14:58:04,8,183848,56872,cap-wifi5,-58,103200000,206500000,2800,2960,01:05:35,00:00:00
|
||||||
|
1788526686,14:58:06,14:58:06,9,305320,47576,cap-wifi5,-58,103200000,206500000,2800,2960,01:05:37,00:00:00
|
||||||
|
1788526688,14:58:08,14:58:08,14,331408,59856,cap-wifi5,-59,103200000,229400000,2800,2960,01:05:39,00:00:00
|
||||||
|
1788526690,14:58:10,14:58:10,17,322448,65576,cap-wifi5,-58,103200000,206500000,2800,2960,01:05:41,00:00:00
|
||||||
|
1788526692,14:58:12,RECONNECT
|
||||||
|
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-dauerping/sampler-s22.csv (Takt 2s, Client 2A:0D:B0:5D:AD:A7)
|
||||||
2397
docs/messdaten/tp25-runde-dauerping/archiv-150553/walk-ping-ipad.csv
Normal file
2397
docs/messdaten/tp25-runde-dauerping/archiv-150553/walk-ping-ipad.csv
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
2398
docs/messdaten/tp25-runde-dauerping/archiv-150553/walk-ping-s22.csv
Normal file
2398
docs/messdaten/tp25-runde-dauerping/archiv-150553/walk-ping-s22.csv
Normal file
File diff suppressed because it is too large
Load Diff
4
docs/messdaten/tp25-runde-dauerping/geraete.txt
Normal file
4
docs/messdaten/tp25-runde-dauerping/geraete.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
iphone|10.40.10.196|F2:62:4C:E1:AF:09
|
||||||
|
s22|10.40.10.179|2A:0D:B0:5D:AD:A7
|
||||||
|
ipad|10.40.10.180|CA:B8:D4:3F:8A:81
|
||||||
|
macbook|10.40.10.82|56:71:6C:22:E6:A9
|
||||||
0
docs/messdaten/tp25-runde-dauerping/konsole.log
Normal file
0
docs/messdaten/tp25-runde-dauerping/konsole.log
Normal file
6
docs/messdaten/tp25-runde-dauerping/markers.csv
Normal file
6
docs/messdaten/tp25-runde-dauerping/markers.csv
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
ts_epoch,ts_local,marker
|
||||||
|
1788527256,15:07:36,PINM=off
|
||||||
|
1788527283,15:08:03,PINM=off
|
||||||
|
1788527311,15:08:31,PINM=off
|
||||||
|
1788527320,15:08:40,PINM=off
|
||||||
|
1788527414,15:10:14,STOP
|
||||||
|
1
docs/messdaten/tp25-runde-dauerping/phase.json
Normal file
1
docs/messdaten/tp25-runde-dauerping/phase.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"mode": "langzeit", "phase": "GEHWEG", "title": "Roaming-Messung l\u00e4uft", "instruction": "Aufzeichnung gestoppt.", "step": 0, "steps": 0, "ends": 0}
|
||||||
131
docs/messdaten/tp25-runde-dauerping/sampler-ipad.csv
Normal file
131
docs/messdaten/tp25-runde-dauerping/sampler-ipad.csv
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788527155,15:05:55,15:05:55,26,110192,66624,cap-wifi3,-58,6000000,216700000,0,0,00:07:03,00:00:00
|
||||||
|
1788527157,15:05:57,15:05:57,10,248200,181784,cap-wifi3,-58,6000000,216700000,0,0,00:07:05,00:00:00
|
||||||
|
1788527159,15:05:59,15:05:59,27,895392,176224,cap-wifi3,-54,309700000,455800000,100936,106200,00:07:07,00:00:00
|
||||||
|
1788527161,15:06:01,15:06:01,5,271768,3546664,cap-wifi3,-55,309700000,309700000,4800,18208,00:07:09,00:00:00
|
||||||
|
1788527163,15:06:03,15:06:03,7,212384,57592,cap-wifi3,-55,309700000,309700000,4800,18208,00:07:11,00:00:00
|
||||||
|
1788527165,15:06:05,15:06:05,6,119880,34152,cap-wifi3,-52,309700000,455800000,62952,10824,00:07:13,00:00:00
|
||||||
|
1788527167,15:06:07,15:06:07,6,285512,62760,cap-wifi3,-48,309700000,412900000,2800,2960,00:07:15,00:00:00
|
||||||
|
1788527169,15:06:09,15:06:09,18,135200,33432,cap-wifi3,-48,309700000,412900000,2800,2960,00:07:17,00:00:00
|
||||||
|
1788527171,15:06:11,15:06:11,2,80168,35552,cap-wifi3,-50,309700000,433300000,2800,3360,00:07:19,00:00:00
|
||||||
|
1788527173,15:06:13,15:06:13,6,263824,45616,cap-wifi3,-49,309700000,455800000,2800,2960,00:07:21,00:00:00
|
||||||
|
1788527175,15:06:15,15:06:15,4,139448,79056,cap-wifi3,-49,309700000,455800000,2800,2960,00:07:23,00:00:00
|
||||||
|
1788527177,15:06:17,15:06:17,7,125944,34792,cap-wifi3,-51,309700000,487500000,2800,2960,00:07:25,00:00:00
|
||||||
|
1788527179,15:06:19,15:06:19,21,228896,34848,cap-wifi3,-51,309700000,516200000,3360,3552,00:07:27,00:00:00
|
||||||
|
1788527181,15:06:21,15:06:21,6,91344,100616,cap-wifi3,-51,309700000,516200000,3360,3552,00:07:29,00:00:00
|
||||||
|
1788527183,15:06:23,15:06:23,8,1161368,460544,cap-wifi3,-52,309700000,541700000,2800,3520,00:07:31,00:00:00
|
||||||
|
1788527185,15:06:25,15:06:25,9,669424,189984,cap-wifi3,-51,309700000,433300000,2800,2960,00:07:33,00:00:00
|
||||||
|
1788527187,15:06:27,15:06:27,8,220472,72928,cap-wifi3,-51,309700000,433300000,2800,2960,00:07:35,00:00:00
|
||||||
|
1788527189,15:06:29,15:06:29,19,85696,19672,cap-wifi3,-50,309700000,455800000,11872,3328,00:07:37,00:00:00
|
||||||
|
1788527191,15:06:31,15:06:31,7,88056,17368,cap-wifi3,-52,309700000,516200000,3360,3552,00:07:39,00:00:00
|
||||||
|
1788527193,15:06:33,15:06:33,7,78232,15688,cap-wifi3,-52,309700000,516200000,3360,3552,00:07:41,00:00:00
|
||||||
|
1788527195,15:06:35,15:06:35,7,77824,19488,cap-wifi3,-50,309700000,433300000,2800,2960,00:07:43,00:00:00
|
||||||
|
1788527197,15:06:37,15:06:37,11,91648,29960,cap-wifi3,-56,309700000,455800000,2800,2960,00:07:45,00:00:00
|
||||||
|
1788527199,15:06:39,15:06:39,19,479584,403096,cap-wifi3,-54,309700000,325000000,2800,2960,00:07:47,00:00:00
|
||||||
|
1788527201,15:06:41,15:06:41,6,83504,18032,cap-wifi3,-54,309700000,325000000,2800,2960,00:07:49,00:00:00
|
||||||
|
1788527203,15:06:43,15:06:43,8,94312,27480,cap-wifi3,-53,309700000,344100000,3248,2960,00:07:51,00:00:00
|
||||||
|
1788527205,15:06:45,15:06:45,7,139816,113728,cap-wifi3,-52,309700000,390000000,2800,2960,00:07:53,00:00:00
|
||||||
|
1788527207,15:06:47,15:06:47,8,77512,23304,cap-wifi3,-52,309700000,390000000,2800,2960,00:07:55,00:00:00
|
||||||
|
1788527209,15:06:49,15:06:49,22,135424,86176,cap-wifi3,-53,309700000,412900000,2800,3360,00:07:57,00:00:00
|
||||||
|
1788527211,15:06:51,15:06:52,6,80072,20128,cap-wifi3,-52,309700000,433300000,2800,2960,00:07:59,00:00:00
|
||||||
|
1788527213,15:06:53,15:06:54,7,81544,23592,cap-wifi3,-52,309700000,433300000,2800,2960,00:08:01,00:00:00
|
||||||
|
1788527216,15:06:56,15:06:56,8,118248,77704,cap-wifi3,-52,309700000,455800000,3808,3552,00:08:03,00:00:00
|
||||||
|
1788527218,15:06:58,15:06:58,6,115296,40584,cap-wifi3,-54,309700000,487500000,2800,2960,00:08:05,00:00:00
|
||||||
|
1788527220,15:07:00,15:07:00,36,72144,17920,cap-wifi3,-54,309700000,487500000,2800,2960,00:08:07,00:00:00
|
||||||
|
1788527222,15:07:02,15:07:02,6,87272,19464,cap-wifi3,-53,309700000,516200000,3808,3920,00:08:09,00:00:00
|
||||||
|
1788527224,15:07:04,15:07:04,7,341584,177744,cap-wifi3,-54,309700000,487500000,3488,2960,00:08:11,00:00:00
|
||||||
|
1788527226,15:07:06,15:07:06,5,96008,29072,cap-wifi3,-54,309700000,487500000,3488,2960,00:08:14,00:00:00
|
||||||
|
1788527228,15:07:08,15:07:08,9,88184,20760,cap-wifi3,-54,309700000,516200000,2800,2960,00:08:16,00:00:00
|
||||||
|
1788527230,15:07:10,15:07:10,19,245616,37632,cap-wifi3,-54,309700000,541700000,3424,2960,00:08:18,00:00:00
|
||||||
|
1788527232,15:07:12,15:07:12,7,136904,41680,cap-wifi3,-54,309700000,541700000,3424,2960,00:08:20,00:00:00
|
||||||
|
1788527234,15:07:14,15:07:14,5,176432,193240,cap-wifi3,-54,309700000,573500000,3360,2960,00:08:22,00:00:00
|
||||||
|
1788527236,15:07:16,15:07:16,5,668616,412672,cap-wifi3,-52,309700000,573500000,2800,2960,00:08:24,00:00:00
|
||||||
|
1788527238,15:07:18,15:07:18,12,10040200,401976,cap-wifi3,-52,309700000,573500000,2800,2960,00:08:26,00:00:00
|
||||||
|
1788527240,15:07:20,15:07:20,21,6694840,181608,cap-wifi3,-52,309700000,573500000,2800,2960,00:08:28,00:00:00
|
||||||
|
1788527242,15:07:22,15:07:22,8,269904,95904,cap-wifi3,-52,309700000,573500000,2800,2960,00:08:30,00:00:00
|
||||||
|
1788527244,15:07:24,15:07:24,10,97040,38768,cap-wifi3,-52,309700000,573500000,2800,2960,00:08:32,00:00:00
|
||||||
|
1788527246,15:07:26,15:07:26,8,265536,57040,cap-wifi3,-51,309700000,573500000,2800,2960,00:08:34,00:00:00
|
||||||
|
1788527248,15:07:28,15:07:28,8,79496,17712,cap-wifi3,-52,309700000,573500000,3360,8208,00:08:36,00:00:00
|
||||||
|
1788527250,15:07:30,15:07:30,5,82824,26656,cap-wifi3,-52,309700000,573500000,3360,8208,00:08:38,00:00:00
|
||||||
|
1788527252,15:07:32,15:07:32,8,79440,24984,cap-wifi3,-52,309700000,573500000,3360,7808,00:08:40,00:00:00
|
||||||
|
1788527254,15:07:34,15:07:34,7,84240,17432,cap-wifi3,-52,309700000,573500000,5248,2960,00:08:42,00:00:00
|
||||||
|
1788527256,15:07:36,15:07:36,15,74928,18888,cap-wifi3,-52,309700000,573500000,5248,2960,00:08:44,00:00:00
|
||||||
|
1788527258,15:07:38,15:07:38,6,79856,17376,cap-wifi3,-51,309700000,573500000,3328,3520,00:08:46,00:00:00
|
||||||
|
1788527260,15:07:40,15:07:40,5,91680,31720,cap-wifi3,-52,275300000,573500000,2800,2960,00:08:48,00:00:00
|
||||||
|
1788527262,15:07:42,15:07:42,6,82760,21192,cap-wifi3,-52,275300000,573500000,2800,2960,00:08:50,00:00:00
|
||||||
|
1788527264,15:07:44,15:07:44,7,91960,31680,cap-wifi3,-51,275300000,573500000,2800,2960,00:08:52,00:00:00
|
||||||
|
1788527266,15:07:46,15:07:46,6,986832,256184,cap-wifi3,-51,275300000,573500000,3920,4144,00:08:54,00:00:00
|
||||||
|
1788527268,15:07:48,15:07:48,7,204344,305984,cap-wifi3,-51,275300000,573500000,3920,4144,00:08:56,00:00:00
|
||||||
|
1788527270,15:07:50,15:07:50,9,90376,25984,cap-wifi3,-52,258100000,573500000,2240,2368,00:08:58,00:00:00
|
||||||
|
1788527272,15:07:52,15:07:52,5,106024,28632,cap-wifi3,-52,258100000,286800000,2800,2960,00:09:00,00:00:00
|
||||||
|
1788527274,15:07:54,15:07:54,8,90816,27368,cap-wifi3,-52,258100000,286800000,2800,2960,00:09:02,00:00:00
|
||||||
|
1788527276,15:07:56,15:07:56,9,80656,22520,cap-wifi3,-52,258100000,573500000,2800,2960,00:09:04,00:00:00
|
||||||
|
1788527278,15:07:58,15:07:58,8,74792,15952,cap-wifi3,-54,258100000,412900000,2800,2960,00:09:06,00:00:00
|
||||||
|
1788527280,15:08:00,15:08:00,9,97280,32008,cap-wifi3,-54,258100000,412900000,2800,2960,00:09:08,00:00:00
|
||||||
|
1788527282,15:08:02,15:08:02,22,405584,131400,cap-wifi3,-55,258100000,412900000,2800,2960,00:09:10,00:00:00
|
||||||
|
1788527284,15:08:04,15:08:04,15,99616,29344,cap-wifi3,-53,258100000,433300000,2800,2960,00:09:12,00:00:00
|
||||||
|
1788527286,15:08:06,15:08:06,3,116040,34784,cap-wifi3,-53,258100000,433300000,2800,2960,00:09:14,00:00:00
|
||||||
|
1788527288,15:08:08,15:08:08,6,263752,46256,cap-wifi3,-54,258100000,455800000,2800,3360,00:09:16,00:00:00
|
||||||
|
1788527290,15:08:10,15:08:10,5,154688,74104,cap-wifi3,-54,258100000,487500000,2800,2960,00:09:18,00:00:00
|
||||||
|
1788527292,15:08:12,15:08:13,4,437800,144600,cap-wifi3,-54,258100000,487500000,2800,2960,00:09:20,00:00:00
|
||||||
|
1788527294,15:08:14,15:08:15,5,273784,69248,cap-wifi3,-56,258100000,390000000,3808,3552,00:09:22,00:00:00
|
||||||
|
1788527297,15:08:17,15:08:17,4,110840,33232,cap-wifi3,-56,258100000,433300000,2800,2960,00:09:24,00:00:00
|
||||||
|
1788527299,15:08:19,15:08:19,5,80584,34112,cap-wifi3,-55,258100000,455800000,3360,3552,00:09:26,00:00:00
|
||||||
|
1788527301,15:08:21,15:08:21,5,89992,25208,cap-wifi3,-55,258100000,455800000,3360,3552,00:09:28,00:00:00
|
||||||
|
1788527303,15:08:23,15:08:23,5,258520,122376,cap-wifi3,-56,258100000,433300000,2800,2960,00:09:30,00:00:00
|
||||||
|
1788527305,15:08:25,15:08:25,8,484544,159104,cap-wifi3,-55,258100000,455800000,2800,2960,00:09:33,00:00:00
|
||||||
|
1788527307,15:08:27,15:08:27,5,85832,21400,cap-wifi3,-55,258100000,455800000,2800,2960,00:09:35,00:00:00
|
||||||
|
1788527309,15:08:29,15:08:29,7,86968,23984,cap-wifi3,-56,258100000,433300000,2800,2960,00:09:37,00:00:00
|
||||||
|
1788527311,15:08:31,15:08:31,4,92848,27216,cap-wifi3,-57,258100000,455800000,2800,2960,00:09:39,00:00:00
|
||||||
|
1788527313,15:08:33,15:08:33,13,88112,30272,cap-wifi3,-57,258100000,455800000,2800,2960,00:09:41,00:00:00
|
||||||
|
1788527315,15:08:35,15:08:35,6,97568,38688,cap-wifi3,-57,258100000,433300000,2800,2960,00:09:43,00:00:00
|
||||||
|
1788527317,15:08:37,15:08:37,7,69376,15424,cap-wifi3,-58,258100000,455800000,3360,3552,00:09:45,00:00:00
|
||||||
|
1788527319,15:08:39,15:08:39,22,95672,27872,cap-wifi3,-58,258100000,455800000,3360,3552,00:09:47,00:00:00
|
||||||
|
1788527321,15:08:41,15:08:41,20,81008,14064,cap-wifi3,-58,258100000,433300000,2800,2960,00:09:49,00:00:00
|
||||||
|
1788527323,15:08:43,15:08:43,9,79480,19824,cap-wifi3,-59,258100000,325000000,3328,2960,00:09:51,00:00:00
|
||||||
|
1788527325,15:08:45,15:08:45,11,83080,28888,cap-wifi3,-59,258100000,325000000,3328,2960,00:09:53,00:00:00
|
||||||
|
1788527327,15:08:47,15:08:47,7,77224,19568,cap-wifi3,-59,258100000,390000000,3360,3952,00:09:55,00:00:00
|
||||||
|
1788527329,15:08:49,15:08:49,20,87976,23544,cap-wifi3,-58,258100000,412900000,2800,2960,00:09:57,00:00:00
|
||||||
|
1788527331,15:08:51,15:08:51,6,99288,43960,cap-wifi3,-58,258100000,412900000,2800,2960,00:09:59,00:00:00
|
||||||
|
1788527333,15:08:53,15:08:53,4,78784,16504,cap-wifi3,-58,258100000,390000000,3808,3552,00:10:01,00:00:00
|
||||||
|
1788527335,15:08:55,15:08:55,8,100664,43024,cap-wifi3,-52,6000000,412900000,3360,3552,00:10:03,00:00:00
|
||||||
|
1788527337,15:08:57,15:08:57,6,75392,68792,cap-wifi3,-52,6000000,412900000,3360,3552,00:10:05,00:00:00
|
||||||
|
1788527339,15:08:59,15:08:59,18,84648,23296,cap-wifi3,-52,258100000,433300000,2800,3360,00:10:07,00:00:00
|
||||||
|
1788527341,15:09:01,15:09:01,5,146608,49608,cap-wifi3,-53,258100000,455800000,2800,2960,00:10:09,00:00:00
|
||||||
|
1788527343,15:09:03,15:09:03,6,271512,52760,cap-wifi3,-53,258100000,455800000,2800,2960,00:10:11,00:00:00
|
||||||
|
1788527345,15:09:05,15:09:05,6,400696,3660640,cap-wifi3,-56,258100000,433300000,2800,2960,00:10:13,00:00:00
|
||||||
|
1788527347,15:09:07,15:09:07,6,131696,25096,cap-wifi3,-55,258100000,390000000,2800,2960,00:10:15,00:00:00
|
||||||
|
1788527349,15:09:09,15:09:09,19,213416,92736,cap-wifi3,-55,258100000,390000000,2800,2960,00:10:17,00:00:00
|
||||||
|
1788527351,15:09:11,15:09:11,6,319104,63216,cap-wifi3,-52,6000000,309700000,2800,2960,00:10:19,00:00:00
|
||||||
|
1788527353,15:09:13,15:09:13,8,153896,48144,cap-wifi3,-54,258100000,344100000,3360,3552,00:10:21,00:00:00
|
||||||
|
1788527355,15:09:15,15:09:15,5,190952,58360,cap-wifi3,-54,258100000,344100000,3360,3552,00:10:23,00:00:00
|
||||||
|
1788527357,15:09:17,15:09:17,7,232928,149512,cap-wifi3,-55,258100000,390000000,4080,6912,00:10:25,00:00:00
|
||||||
|
1788527359,15:09:19,15:09:20,19,108000,26560,cap-wifi3,-56,258100000,412900000,3360,3552,00:10:27,00:00:00
|
||||||
|
1788527361,15:09:21,15:09:22,10,2754208,213840,cap-wifi3,-56,258100000,412900000,3360,3552,00:10:29,00:00:00
|
||||||
|
1788527364,15:09:24,15:09:24,7,314040,73608,cap-wifi3,-55,258100000,216700000,15040,7600,00:10:31,00:00:00
|
||||||
|
1788527366,15:09:26,15:09:26,5,485488,100744,cap-wifi3,-54,258100000,229400000,3304,6992,00:10:33,00:00:00
|
||||||
|
1788527368,15:09:28,15:09:28,6,123816,28072,cap-wifi3,-54,258100000,229400000,3304,6992,00:10:35,00:00:00
|
||||||
|
1788527370,15:09:30,15:09:30,19,124944,35616,cap-wifi3,-56,258100000,433300000,2800,2960,00:10:37,00:00:00
|
||||||
|
1788527372,15:09:32,15:09:32,5,171352,44496,cap-wifi3,-54,258100000,433300000,4928,0,00:10:39,00:00:00
|
||||||
|
1788527374,15:09:34,15:09:34,6,166240,43784,cap-wifi3,-54,6000000,433300000,2800,0,00:10:42,00:00:00
|
||||||
|
1788527376,15:09:36,15:09:36,6,150760,49464,cap-wifi3,-54,6000000,433300000,2800,0,00:10:44,00:00:00
|
||||||
|
1788527378,15:09:38,15:09:38,10,185768,47120,cap-wifi3,-55,258100000,433300000,2800,6496,00:10:46,00:00:00
|
||||||
|
1788527380,15:09:40,15:09:40,18,190032,49512,cap-wifi3,-54,258100000,433300000,2240,0,00:10:48,00:00:00
|
||||||
|
1788527382,15:09:42,15:09:42,8,97808,22304,cap-wifi3,-54,258100000,433300000,2240,0,00:10:50,00:00:00
|
||||||
|
1788527384,15:09:44,15:09:44,8,148400,53672,cap-wifi3,-54,6000000,433300000,13464,368,00:10:52,00:00:00
|
||||||
|
1788527386,15:09:46,15:09:46,2,97568,37160,cap-wifi3,-54,258100000,455800000,2800,2960,00:10:54,00:00:00
|
||||||
|
1788527388,15:09:48,15:09:48,4,76456,19280,cap-wifi3,-54,258100000,455800000,2800,2960,00:10:56,00:00:00
|
||||||
|
1788527390,15:09:50,15:09:50,9,74488,14496,cap-wifi3,-54,258100000,390000000,2928,0,00:10:58,00:00:00
|
||||||
|
1788527392,15:09:52,15:09:52,9,81688,114120,cap-wifi3,-54,258100000,390000000,2240,0,00:11:00,00:00:00
|
||||||
|
1788527394,15:09:54,15:09:54,11,139472,94312,cap-wifi3,-54,258100000,390000000,2240,0,00:11:02,00:00:00
|
||||||
|
1788527396,15:09:56,15:09:56,8,99376,34800,cap-wifi3,-54,258100000,390000000,2240,0,00:11:04,00:00:00
|
||||||
|
1788527398,15:09:58,15:09:58,7,95624,20936,cap-wifi3,-54,258100000,390000000,2800,0,00:11:06,00:00:00
|
||||||
|
1788527400,15:10:00,15:10:00,11,75688,17080,cap-wifi3,-54,258100000,390000000,2800,0,00:11:08,00:00:00
|
||||||
|
1788527402,15:10:02,15:10:02,8,194448,59984,cap-wifi3,-54,258100000,390000000,2240,0,00:11:10,00:00:00
|
||||||
|
1788527404,15:10:04,15:10:04,5,87848,23392,cap-wifi3,-54,258100000,390000000,5040,0,00:11:12,00:00:00
|
||||||
|
1788527406,15:10:06,15:10:06,4,102440,31824,cap-wifi3,-54,258100000,390000000,5040,0,00:11:14,00:00:00
|
||||||
|
1788527408,15:10:08,15:10:08,7,140352,49312,cap-wifi3,-54,258100000,390000000,2800,0,00:11:16,00:00:00
|
||||||
|
1788527410,15:10:10,15:10:10,20,81216,18128,cap-wifi3,-53,258100000,390000000,2688,0,00:11:18,00:00:00
|
||||||
|
1788527412,15:10:12,15:10:12,8,223088,23248,cap-wifi3,-53,258100000,390000000,2688,0,00:11:20,00:00:00
|
||||||
|
1788527414,15:10:14,15:10:14,7,139784,51840,cap-wifi3,-54,258100000,390000000,2240,0,00:11:22,00:00:00
|
||||||
|
1788527415,15:10:15,RECONNECT
|
||||||
|
1
docs/messdaten/tp25-runde-dauerping/sampler-ipad.out
Normal file
1
docs/messdaten/tp25-runde-dauerping/sampler-ipad.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-dauerping/sampler-ipad.csv (Takt 2s, Client CA:B8:D4:3F:8A:81)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
14:58:12 Verbindung weg -- neuer Versuch in 2 s
|
||||||
|
15:10:15 Verbindung weg -- neuer Versuch in 2 s
|
||||||
130
docs/messdaten/tp25-runde-dauerping/sampler-iphone.csv
Normal file
130
docs/messdaten/tp25-runde-dauerping/sampler-iphone.csv
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788527155,15:05:55,RECONNECT
|
||||||
|
1788527159,15:05:59,15:05:59,26,895392,176224,cap-wifi3,-63,154900000,309700000,15624,7104,00:13:12,00:00:00
|
||||||
|
1788527161,15:06:01,15:06:01,12,271768,3546664,cap-wifi3,-61,154900000,325000000,4576,3552,00:13:14,00:00:00
|
||||||
|
1788527163,15:06:03,15:06:03,11,212384,57592,cap-wifi3,-61,154900000,325000000,4576,3552,00:13:16,00:00:00
|
||||||
|
1788527165,15:06:05,15:06:05,6,119880,34152,cap-wifi3,-59,154900000,325000000,2240,0,00:13:18,00:00:00
|
||||||
|
1788527167,15:06:07,15:06:07,6,285512,62760,cap-wifi3,-58,154900000,325000000,3360,0,00:13:20,00:00:00
|
||||||
|
1788527169,15:06:09,15:06:09,18,135200,33432,cap-wifi3,-58,154900000,325000000,3360,0,00:13:22,00:00:00
|
||||||
|
1788527171,15:06:11,15:06:11,2,80168,35552,cap-wifi3,-58,154900000,325000000,2240,0,00:13:24,00:00:00
|
||||||
|
1788527173,15:06:13,15:06:13,6,263824,45616,cap-wifi3,-57,154900000,325000000,3360,0,00:13:26,00:00:00
|
||||||
|
1788527175,15:06:15,15:06:15,4,139448,79056,cap-wifi3,-57,154900000,325000000,3360,0,00:13:28,00:00:00
|
||||||
|
1788527177,15:06:17,15:06:17,7,125944,34792,cap-wifi3,-58,154900000,325000000,1680,0,00:13:30,00:00:00
|
||||||
|
1788527179,15:06:19,15:06:19,21,228896,34848,cap-wifi3,-60,154900000,325000000,0,0,00:13:32,00:00:00
|
||||||
|
1788527181,15:06:21,15:06:21,6,91344,100616,cap-wifi3,-60,154900000,325000000,0,0,00:13:34,00:00:00
|
||||||
|
1788527183,15:06:23,15:06:23,8,1161368,460544,cap-wifi3,-62,154900000,325000000,4368,4592,00:13:36,00:00:00
|
||||||
|
1788527185,15:06:25,15:06:25,9,669424,189984,cap-wifi3,-56,154900000,344100000,2800,3712,00:13:38,00:00:00
|
||||||
|
1788527187,15:06:27,15:06:27,8,220472,72928,cap-wifi3,-56,154900000,344100000,2800,3712,00:13:40,00:00:00
|
||||||
|
1788527189,15:06:29,15:06:29,19,85696,19672,cap-wifi3,-60,154900000,325000000,4880,10808,00:13:42,00:00:00
|
||||||
|
1788527191,15:06:31,15:06:31,7,80008,30360,cap-wifi3,-48,154900000,309700000,4080,4992,00:13:44,00:00:00
|
||||||
|
1788527193,15:06:33,15:06:33,7,85392,35808,cap-wifi3,-48,154900000,309700000,4080,4992,00:13:46,00:00:00
|
||||||
|
1788527195,15:06:35,15:06:35,7,81000,33304,cap-wifi3,-52,154900000,325000000,2800,2960,00:13:48,00:00:00
|
||||||
|
1788527197,15:06:37,15:06:37,11,80840,16392,cap-wifi3,-60,154900000,344100000,2800,2960,00:13:50,00:00:00
|
||||||
|
1788527199,15:06:39,15:06:39,19,479584,403096,cap-wifi3,-60,154900000,344100000,2800,2960,00:13:52,00:00:00
|
||||||
|
1788527201,15:06:41,15:06:41,6,83504,18032,cap-wifi3,-54,154900000,390000000,2800,2960,00:13:54,00:00:00
|
||||||
|
1788527203,15:06:43,15:06:43,8,94312,27480,cap-wifi3,-52,154900000,412900000,3776,3552,00:13:56,00:00:00
|
||||||
|
1788527205,15:06:45,15:06:45,7,139816,113728,cap-wifi3,-54,154900000,455800000,3360,3552,00:13:58,00:00:00
|
||||||
|
1788527207,15:06:47,15:06:47,8,77512,23304,cap-wifi3,-54,154900000,455800000,3360,3552,00:14:00,00:00:00
|
||||||
|
1788527209,15:06:49,15:06:49,22,135424,86176,cap-wifi3,-53,206500000,455800000,2800,2960,00:14:02,00:00:00
|
||||||
|
1788527211,15:06:51,15:06:51,6,80072,20128,cap-wifi3,-53,12000000,516200000,2800,2960,00:14:04,00:00:00
|
||||||
|
1788527213,15:06:53,15:06:53,7,81544,23592,cap-wifi3,-53,12000000,516200000,2800,2960,00:14:06,00:00:00
|
||||||
|
1788527215,15:06:55,15:06:55,8,118248,77704,cap-wifi3,-54,206500000,573500000,3248,3552,00:14:08,00:00:00
|
||||||
|
1788527217,15:06:57,15:06:58,6,115296,40584,cap-wifi3,-53,206500000,573500000,3360,3552,00:14:10,00:00:00
|
||||||
|
1788527219,15:06:59,15:07:00,36,72144,17920,cap-wifi3,-53,206500000,573500000,3360,3552,00:14:12,00:00:00
|
||||||
|
1788527221,15:07:01,15:07:02,6,87272,19464,cap-wifi3,-55,206500000,412900000,2800,2960,00:14:14,00:00:00
|
||||||
|
1788527224,15:07:04,15:07:04,7,341584,177744,cap-wifi3,-55,206500000,455800000,2800,2960,00:14:16,00:00:00
|
||||||
|
1788527226,15:07:06,15:07:06,5,96008,29072,cap-wifi3,-55,206500000,455800000,2800,2960,00:14:18,00:00:00
|
||||||
|
1788527228,15:07:08,15:07:08,9,88184,20760,cap-wifi3,-54,206500000,455800000,2800,2960,00:14:21,00:00:00
|
||||||
|
1788527230,15:07:10,15:07:10,19,245616,37632,cap-wifi3,-55,206500000,433300000,2800,2960,00:14:23,00:00:00
|
||||||
|
1788527232,15:07:12,15:07:12,7,136904,41680,cap-wifi3,-55,206500000,433300000,2800,2960,00:14:25,00:00:00
|
||||||
|
1788527234,15:07:14,15:07:14,5,176432,193240,cap-wifi3,-54,206500000,455800000,2800,2960,00:14:27,00:00:00
|
||||||
|
1788527236,15:07:16,15:07:16,5,668616,412672,cap-wifi3,-53,206500000,487500000,3360,3552,00:14:29,00:00:00
|
||||||
|
1788527238,15:07:18,15:07:18,12,10040200,401976,cap-wifi3,-53,206500000,487500000,3360,3552,00:14:31,00:00:00
|
||||||
|
1788527240,15:07:20,15:07:20,21,6694840,181608,cap-wifi3,-53,6000000,516200000,2800,3336,00:14:33,00:00:00
|
||||||
|
1788527242,15:07:22,15:07:22,7,269904,95904,cap-wifi3,-53,206500000,487500000,2800,2960,00:14:35,00:00:00
|
||||||
|
1788527244,15:07:24,15:07:24,6,97040,38768,cap-wifi3,-53,206500000,487500000,2800,2960,00:14:37,00:00:00
|
||||||
|
1788527246,15:07:26,15:07:26,6,265536,57040,cap-wifi3,-53,206500000,516200000,3360,3552,00:14:39,00:00:00
|
||||||
|
1788527248,15:07:28,15:07:28,8,79496,17712,cap-wifi3,-52,206500000,487500000,2800,2960,00:14:41,00:00:00
|
||||||
|
1788527250,15:07:30,15:07:30,20,82824,26656,cap-wifi3,-52,206500000,487500000,2800,2960,00:14:43,00:00:00
|
||||||
|
1788527252,15:07:32,15:07:32,8,79440,24984,cap-wifi3,-53,206500000,516200000,2800,2960,00:14:45,00:00:00
|
||||||
|
1788527254,15:07:34,15:07:34,7,84240,17432,cap-wifi3,-51,206500000,541700000,4560,3552,00:14:47,00:00:00
|
||||||
|
1788527256,15:07:36,15:07:36,15,74928,18888,cap-wifi3,-51,206500000,541700000,4560,3552,00:14:49,00:00:00
|
||||||
|
1788527258,15:07:38,15:07:38,6,79856,17376,cap-wifi3,-51,206500000,573500000,2800,2960,00:14:51,00:00:00
|
||||||
|
1788527260,15:07:40,15:07:40,5,91680,31720,cap-wifi3,-51,206500000,573500000,2800,2960,00:14:53,00:00:00
|
||||||
|
1788527262,15:07:42,15:07:42,6,82760,21192,cap-wifi3,-51,206500000,573500000,2800,2960,00:14:55,00:00:00
|
||||||
|
1788527264,15:07:44,15:07:44,7,91960,31680,cap-wifi3,-51,206500000,573500000,2800,2960,00:14:57,00:00:00
|
||||||
|
1788527266,15:07:46,15:07:46,6,986832,256184,cap-wifi3,-51,206500000,573500000,2800,2960,00:14:59,00:00:00
|
||||||
|
1788527268,15:07:48,15:07:48,7,204344,305984,cap-wifi3,-51,206500000,573500000,2800,2960,00:15:01,00:00:00
|
||||||
|
1788527270,15:07:50,15:07:50,9,90376,25984,cap-wifi3,-51,206500000,516200000,3248,3328,00:15:03,00:00:00
|
||||||
|
1788527272,15:07:52,15:07:52,5,106024,28632,cap-wifi3,-50,12000000,516200000,3808,3920,00:15:05,00:00:00
|
||||||
|
1788527274,15:07:54,15:07:54,8,86752,24568,cap-wifi3,-50,12000000,516200000,3808,3920,00:15:07,00:00:00
|
||||||
|
1788527276,15:07:56,15:07:56,9,68568,17040,cap-wifi3,-51,206500000,541700000,2800,2960,00:15:09,00:00:00
|
||||||
|
1788527278,15:07:58,15:07:58,8,104792,53200,cap-wifi3,-51,206500000,390000000,3360,3552,00:15:11,00:00:00
|
||||||
|
1788527280,15:08:00,15:08:00,9,304368,102592,cap-wifi3,-51,206500000,390000000,3360,3552,00:15:13,00:00:00
|
||||||
|
1788527282,15:08:02,15:08:02,22,74432,14824,cap-wifi3,-50,24000000,412900000,2800,2960,00:15:15,00:00:00
|
||||||
|
1788527284,15:08:04,15:08:04,15,99616,29344,cap-wifi3,-51,154900000,433300000,3360,3552,00:15:17,00:00:00
|
||||||
|
1788527286,15:08:06,15:08:06,3,116040,34784,cap-wifi3,-51,154900000,433300000,3360,3552,00:15:19,00:00:00
|
||||||
|
1788527288,15:08:08,15:08:08,6,263752,46256,cap-wifi3,-50,24000000,455800000,2800,2960,00:15:21,00:00:00
|
||||||
|
1788527290,15:08:10,15:08:10,5,154688,74104,cap-wifi3,-51,154900000,487500000,2800,2960,00:15:23,00:00:00
|
||||||
|
1788527292,15:08:12,15:08:12,4,437800,144600,cap-wifi3,-51,154900000,487500000,2800,2960,00:15:25,00:00:00
|
||||||
|
1788527294,15:08:14,15:08:14,5,273784,69248,cap-wifi3,-50,154900000,516200000,3248,3552,00:15:27,00:00:00
|
||||||
|
1788527296,15:08:16,15:08:16,4,110840,33232,cap-wifi3,-50,154900000,541700000,2800,2960,00:15:29,00:00:00
|
||||||
|
1788527298,15:08:18,15:08:18,5,80584,34112,cap-wifi3,-50,154900000,541700000,2800,2960,00:15:31,00:00:00
|
||||||
|
1788527300,15:08:20,15:08:20,5,89992,25208,cap-wifi3,-51,206500000,573500000,3360,3928,00:15:33,00:00:00
|
||||||
|
1788527302,15:08:22,15:08:23,5,258520,122376,cap-wifi3,-50,206500000,573500000,2800,2960,00:15:35,00:00:00
|
||||||
|
1788527304,15:08:24,15:08:25,8,484544,159104,cap-wifi3,-50,206500000,573500000,2800,2960,00:15:37,00:00:00
|
||||||
|
1788527306,15:08:26,15:08:27,5,85832,21400,cap-wifi3,-52,206500000,573500000,6336,6536,00:15:39,00:00:00
|
||||||
|
1788527309,15:08:29,15:08:29,7,86968,23984,cap-wifi3,-51,206500000,573500000,4384,5704,00:15:41,00:00:00
|
||||||
|
1788527311,15:08:31,15:08:31,4,92848,27216,cap-wifi3,-50,24000000,573500000,3360,2960,00:15:44,00:00:00
|
||||||
|
1788527313,15:08:33,15:08:33,13,88112,30272,cap-wifi3,-50,24000000,573500000,3360,2960,00:15:46,00:00:00
|
||||||
|
1788527315,15:08:35,15:08:35,6,97568,38688,cap-wifi3,-51,172100000,573500000,2800,2960,00:15:48,00:00:00
|
||||||
|
1788527317,15:08:37,15:08:37,7,69376,15424,cap-wifi3,-51,172100000,573500000,2800,2960,00:15:50,00:00:00
|
||||||
|
1788527319,15:08:39,15:08:39,7,95672,27872,cap-wifi3,-51,172100000,573500000,2800,2960,00:15:52,00:00:00
|
||||||
|
1788527321,15:08:41,15:08:41,8,81008,14064,cap-wifi3,-52,172100000,573500000,5648,5744,00:15:54,00:00:00
|
||||||
|
1788527323,15:08:43,15:08:43,8,79480,19824,cap-wifi3,-51,172100000,573500000,3360,3552,00:15:56,00:00:00
|
||||||
|
1788527325,15:08:45,15:08:45,6,83080,28888,cap-wifi3,-51,172100000,573500000,3360,3552,00:15:58,00:00:00
|
||||||
|
1788527327,15:08:47,15:08:47,9,77224,19568,cap-wifi3,-52,172100000,573500000,2800,2960,00:16:00,00:00:00
|
||||||
|
1788527329,15:08:49,15:08:49,7,87976,23544,cap-wifi3,-53,172100000,573500000,2800,2960,00:16:02,00:00:00
|
||||||
|
1788527331,15:08:51,15:08:51,6,99288,43960,cap-wifi3,-53,172100000,573500000,2800,2960,00:16:04,00:00:00
|
||||||
|
1788527333,15:08:53,15:08:53,4,78784,16504,cap-wifi3,-56,172100000,455800000,4416,4840,00:16:06,00:00:00
|
||||||
|
1788527335,15:08:55,15:08:55,8,100664,43024,cap-wifi3,-56,154900000,344100000,3408,3656,00:16:08,00:00:00
|
||||||
|
1788527337,15:08:57,15:08:57,6,75392,68792,cap-wifi3,-56,154900000,344100000,3408,3656,00:16:10,00:00:00
|
||||||
|
1788527339,15:08:59,15:08:59,18,84648,23296,cap-wifi3,-53,154900000,344100000,3936,4496,00:16:12,00:00:00
|
||||||
|
1788527341,15:09:01,15:09:01,5,146608,49608,cap-wifi3,-53,154900000,325000000,3408,3656,00:16:14,00:00:00
|
||||||
|
1788527343,15:09:03,15:09:03,6,271512,52760,cap-wifi3,-53,154900000,325000000,3408,3656,00:16:16,00:00:00
|
||||||
|
1788527345,15:09:05,15:09:05,6,400696,3660640,cap-wifi3,-51,154900000,344100000,3408,2960,00:16:18,00:00:00
|
||||||
|
1788527347,15:09:07,15:09:07,6,131696,25096,cap-wifi3,-58,154900000,325000000,4896,5048,00:16:20,00:00:00
|
||||||
|
1788527349,15:09:09,15:09:09,19,213416,92736,cap-wifi3,-58,154900000,325000000,4896,5048,00:16:22,00:00:00
|
||||||
|
1788527351,15:09:11,15:09:11,6,319104,63216,cap-wifi3,-50,206500000,344100000,3408,3656,00:16:24,00:00:00
|
||||||
|
1788527353,15:09:13,15:09:13,8,153896,48144,cap-wifi3,-46,206500000,412900000,3968,4248,00:16:26,00:00:00
|
||||||
|
1788527355,15:09:15,15:09:15,5,190952,58360,cap-wifi3,-46,206500000,412900000,3968,4248,00:16:28,00:00:00
|
||||||
|
1788527357,15:09:17,15:09:17,7,232928,149512,cap-wifi3,-59,206500000,433300000,3408,3656,00:16:30,00:00:00
|
||||||
|
1788527359,15:09:19,15:09:19,19,108000,26560,cap-wifi3,-60,206500000,344100000,3856,4032,00:16:32,00:00:00
|
||||||
|
1788527361,15:09:21,15:09:21,10,2754208,213840,cap-wifi3,-60,206500000,344100000,3856,4032,00:16:34,00:00:00
|
||||||
|
1788527363,15:09:23,15:09:24,7,314040,73608,cap-wifi3,-60,206500000,325000000,5488,11504,00:16:36,00:00:00
|
||||||
|
1788527365,15:09:25,15:09:26,5,485488,100744,cap-wifi3,-60,206500000,344100000,4736,5096,00:16:38,00:00:00
|
||||||
|
1788527367,15:09:28,15:09:28,6,123816,28072,cap-wifi3,-60,206500000,344100000,4736,5096,00:16:40,00:00:00
|
||||||
|
1788527370,15:09:30,15:09:30,19,124944,35616,cap-wifi3,-61,154900000,292500000,2800,0,00:16:42,00:00:00
|
||||||
|
1788527372,15:09:32,15:09:32,5,171352,44496,cap-wifi3,-60,137600000,325000000,3136,368,00:16:44,00:00:00
|
||||||
|
1788527374,15:09:34,15:09:34,6,166240,43784,cap-wifi3,-60,137600000,325000000,3136,368,00:16:47,00:00:00
|
||||||
|
1788527376,15:09:36,15:09:36,6,150760,49464,cap-wifi3,-55,172100000,309700000,15056,42256,00:16:49,00:00:00
|
||||||
|
1788527378,15:09:38,15:09:38,10,185768,47120,cap-wifi3,-62,172100000,325000000,3408,3656,00:16:51,00:00:00
|
||||||
|
1788527380,15:09:40,15:09:40,18,190032,49512,cap-wifi3,-62,172100000,344100000,3408,3656,00:16:53,00:00:00
|
||||||
|
1788527382,15:09:42,15:09:42,8,97808,22304,cap-wifi3,-62,172100000,344100000,3408,3656,00:16:55,00:00:00
|
||||||
|
1788527384,15:09:44,15:09:44,8,148400,53672,cap-wifi3,-63,12000000,412900000,3856,10808,00:16:57,00:00:00
|
||||||
|
1788527386,15:09:46,15:09:46,2,97568,37160,cap-wifi3,-62,172100000,412900000,2800,2960,00:16:59,00:00:00
|
||||||
|
1788527388,15:09:48,15:09:48,4,76456,19280,cap-wifi3,-62,172100000,412900000,2800,2960,00:17:01,00:00:00
|
||||||
|
1788527390,15:09:50,15:09:50,17,74488,14496,cap-wifi3,-64,172100000,325000000,7688,7440,00:17:03,00:00:00
|
||||||
|
1788527392,15:09:52,15:09:52,4,81688,114120,cap-wifi3,-62,172100000,344100000,3888,4768,00:17:05,00:00:00
|
||||||
|
1788527394,15:09:54,15:09:54,8,139472,94312,cap-wifi3,-62,172100000,344100000,3888,4768,00:17:07,00:00:00
|
||||||
|
1788527396,15:09:56,15:09:56,4,99376,34800,cap-wifi3,-63,172100000,412900000,3328,3424,00:17:09,00:00:00
|
||||||
|
1788527398,15:09:58,15:09:58,5,95624,20936,cap-wifi3,-62,172100000,412900000,3360,3552,00:17:11,00:00:00
|
||||||
|
1788527400,15:10:00,15:10:00,11,75688,17080,cap-wifi3,-62,172100000,412900000,3360,3552,00:17:13,00:00:00
|
||||||
|
1788527402,15:10:02,15:10:02,8,194448,59984,cap-wifi3,-61,172100000,390000000,2800,0,00:17:15,00:00:00
|
||||||
|
1788527404,15:10:04,15:10:04,5,87848,23392,cap-wifi3,-60,172100000,390000000,2128,368,00:17:17,00:00:00
|
||||||
|
1788527406,15:10:06,15:10:06,4,102440,31824,cap-wifi3,-60,172100000,390000000,2128,368,00:17:19,00:00:00
|
||||||
|
1788527408,15:10:08,15:10:08,7,140352,49312,cap-wifi3,-61,172100000,390000000,2240,0,00:17:21,00:00:00
|
||||||
|
1788527410,15:10:10,15:10:10,20,88352,30376,cap-wifi3,-61,137600000,390000000,3248,0,00:17:23,00:00:00
|
||||||
|
1788527412,15:10:12,15:10:12,8,124328,46808,cap-wifi3,-61,137600000,390000000,3248,0,00:17:25,00:00:00
|
||||||
|
1788527414,15:10:14,15:10:14,7,84784,23040,cap-wifi3,-61,137600000,390000000,2800,0,00:17:27,00:00:00
|
||||||
|
1788527415,15:10:15,RECONNECT
|
||||||
|
1
docs/messdaten/tp25-runde-dauerping/sampler-iphone.out
Normal file
1
docs/messdaten/tp25-runde-dauerping/sampler-iphone.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-dauerping/sampler-iphone.csv (Takt 2s, Client F2:62:4C:E1:AF:09)
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
14:58:12 Verbindung weg -- neuer Versuch in 2 s
|
||||||
|
ssh_askpass: exec(/usr/X11R6/bin/ssh-askpass): No such file or directory
|
||||||
|
Permission denied, please try again.
|
||||||
|
ssh_askpass: exec(/usr/X11R6/bin/ssh-askpass): No such file or directory
|
||||||
|
Permission denied, please try again.
|
||||||
|
ssh_askpass: exec(/usr/X11R6/bin/ssh-askpass): No such file or directory
|
||||||
|
admin@10.40.99.1: Permission denied (publickey,password).
|
||||||
|
15:05:55 Verbindung weg -- neuer Versuch in 2 s
|
||||||
|
15:10:15 Verbindung weg -- neuer Versuch in 2 s
|
||||||
130
docs/messdaten/tp25-runde-dauerping/sampler-macbook.csv
Normal file
130
docs/messdaten/tp25-runde-dauerping/sampler-macbook.csv
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788527155,15:05:55,15:05:55,26,215104,164208,cap-wifi3,-59,344100000,455800000,1108432,1179376,00:06:21,00:00:00
|
||||||
|
1788527157,15:05:57,15:05:57,10,230144,103104,cap-wifi3,-58,412900000,455800000,376304,298808,00:06:23,00:00:00
|
||||||
|
1788527159,15:05:59,15:05:59,27,997352,175928,cap-wifi3,-58,412900000,455800000,376304,298808,00:06:25,00:00:00
|
||||||
|
1788527161,15:06:01,15:06:01,5,496624,179032,cap-wifi3,-60,455800000,455800000,2283840,1594024,00:06:27,00:00:00
|
||||||
|
1788527163,15:06:03,15:06:03,7,112272,25664,cap-wifi3,-61,455800000,344100000,71224,20160,00:06:30,00:00:00
|
||||||
|
1788527165,15:06:05,15:06:06,6,353280,101384,cap-wifi3,-61,455800000,344100000,71224,20160,00:06:32,00:00:00
|
||||||
|
1788527167,15:06:08,15:06:08,6,137256,37360,cap-wifi3,-63,344100000,344100000,1205496,674488,00:06:34,00:00:00
|
||||||
|
1788527170,15:06:10,15:06:10,18,373144,1851168,cap-wifi3,-62,344100000,455800000,625632,2151904,00:06:36,00:00:00
|
||||||
|
1788527172,15:06:12,15:06:12,2,79504,15688,cap-wifi3,-62,344100000,455800000,625632,2151904,00:06:38,00:00:00
|
||||||
|
1788527174,15:06:14,15:06:14,6,215576,3512448,cap-wifi3,-61,412900000,412900000,720568,420128,00:06:40,00:00:00
|
||||||
|
1788527176,15:06:16,15:06:16,4,170440,37144,cap-wifi3,-63,344100000,309700000,1040816,801272,00:06:42,00:00:00
|
||||||
|
1788527178,15:06:18,15:06:18,7,187488,1860720,cap-wifi3,-63,344100000,309700000,1040816,801272,00:06:44,00:00:00
|
||||||
|
1788527180,15:06:20,15:06:20,21,418584,3563696,cap-wifi3,-63,412900000,275300000,496424,2170904,00:06:46,00:00:00
|
||||||
|
1788527182,15:06:22,15:06:22,6,91920,32648,cap-wifi3,-64,412900000,275300000,539264,491728,00:06:48,00:00:00
|
||||||
|
1788527184,15:06:24,15:06:24,8,229720,340576,cap-wifi3,-64,412900000,275300000,539264,491728,00:06:50,00:00:00
|
||||||
|
1788527186,15:06:26,15:06:26,11,324680,63616,cap-wifi3,-63,412900000,344100000,768576,455936,00:06:52,00:00:00
|
||||||
|
1788527188,15:06:28,15:06:28,7,323944,57120,cap-wifi3,-67,344100000,309700000,515712,335688,00:06:54,00:00:00
|
||||||
|
1788527190,15:06:30,15:06:30,7,89536,26840,cap-wifi3,-67,344100000,309700000,515712,335688,00:06:56,00:00:00
|
||||||
|
1788527192,15:06:32,15:06:32,6,88056,17368,cap-wifi3,-62,344100000,344100000,546008,422232,00:06:58,00:00:00
|
||||||
|
1788527194,15:06:34,15:06:34,6,78232,15688,cap-wifi3,-62,412900000,344100000,403184,296104,00:07:00,00:00:00
|
||||||
|
1788527196,15:06:36,15:06:36,6,77824,19488,cap-wifi3,-62,412900000,344100000,403184,296104,00:07:02,00:00:00
|
||||||
|
1788527198,15:06:38,15:06:38,8,91648,29960,cap-wifi3,-62,412900000,344100000,665616,416832,00:07:04,00:00:00
|
||||||
|
1788527200,15:06:40,15:06:40,6,479584,403096,cap-wifi3,-63,344100000,275300000,1141120,678936,00:07:06,00:00:00
|
||||||
|
1788527202,15:06:42,15:06:42,5,83504,18032,cap-wifi3,-63,344100000,275300000,1141120,678936,00:07:08,00:00:00
|
||||||
|
1788527204,15:06:44,15:06:44,12,94312,27480,cap-wifi3,-64,344100000,344100000,511368,235632,00:07:10,00:00:00
|
||||||
|
1788527206,15:06:46,15:06:46,11,104592,25288,cap-wifi3,-65,412900000,309700000,882160,555064,00:07:12,00:00:00
|
||||||
|
1788527208,15:06:48,15:06:48,9,77672,15824,cap-wifi3,-65,344100000,275300000,71864,300800,00:07:14,00:00:00
|
||||||
|
1788527210,15:06:50,15:06:50,8,130536,65520,cap-wifi3,-65,344100000,275300000,71864,300800,00:07:16,00:00:00
|
||||||
|
1788527212,15:06:52,15:06:52,6,179704,71320,cap-wifi3,-65,309700000,344100000,612752,430056,00:07:19,00:00:00
|
||||||
|
1788527214,15:06:54,15:06:54,7,150376,135320,cap-wifi3,-64,309700000,309700000,556768,426072,00:07:21,00:00:00
|
||||||
|
1788527216,15:06:56,15:06:57,7,180576,117008,cap-wifi3,-64,309700000,309700000,556768,426072,00:07:23,00:00:00
|
||||||
|
1788527219,15:06:59,15:06:59,9,81080,17128,cap-wifi3,-65,344100000,412900000,687448,312096,00:07:25,00:00:00
|
||||||
|
1788527221,15:07:01,15:07:01,6,267072,71760,cap-wifi3,-64,412900000,309700000,1456168,436368,00:07:27,00:00:00
|
||||||
|
1788527223,15:07:03,15:07:03,7,229720,79648,cap-wifi3,-64,412900000,309700000,1456168,436368,00:07:29,00:00:00
|
||||||
|
1788527225,15:07:05,15:07:05,12,613656,242376,cap-wifi3,-65,412900000,344100000,643504,429016,00:07:31,00:00:00
|
||||||
|
1788527227,15:07:07,15:07:07,9,136256,40080,cap-wifi3,-64,412900000,344100000,619904,419664,00:07:33,00:00:00
|
||||||
|
1788527229,15:07:09,15:07:09,11,113040,49536,cap-wifi3,-64,412900000,344100000,619904,419664,00:07:35,00:00:00
|
||||||
|
1788527231,15:07:11,15:07:11,7,85432,17976,cap-wifi3,-64,412900000,309700000,1090376,738848,00:07:37,00:00:00
|
||||||
|
1788527233,15:07:13,15:07:13,5,81944,26128,cap-wifi3,-65,412900000,344100000,947112,611480,00:07:39,00:00:00
|
||||||
|
1788527235,15:07:15,15:07:15,5,954264,397144,cap-wifi3,-65,412900000,344100000,947112,611480,00:07:41,00:00:00
|
||||||
|
1788527237,15:07:17,15:07:17,12,3430312,274976,cap-wifi3,-64,412900000,344100000,1132848,732040,00:07:43,00:00:00
|
||||||
|
1788527239,15:07:19,15:07:19,21,5774264,240088,cap-wifi3,-63,412900000,344100000,886256,615784,00:07:45,00:00:00
|
||||||
|
1788527241,15:07:21,15:07:21,7,419688,124632,cap-wifi3,-63,412900000,344100000,886256,615784,00:07:47,00:00:00
|
||||||
|
1788527243,15:07:23,15:07:23,6,146872,47152,cap-wifi3,-64,412900000,344100000,588616,494568,00:07:49,00:00:00
|
||||||
|
1788527245,15:07:25,15:07:25,6,469520,144920,cap-wifi3,-62,412900000,344100000,576512,462856,00:07:51,00:00:00
|
||||||
|
1788527247,15:07:27,15:07:27,8,87480,21512,cap-wifi3,-62,412900000,344100000,576512,462856,00:07:53,00:00:00
|
||||||
|
1788527249,15:07:29,15:07:29,20,89592,20496,cap-wifi3,-63,455800000,344100000,256840,24640,00:07:55,00:00:00
|
||||||
|
1788527251,15:07:31,15:07:31,9,77304,18144,cap-wifi3,-63,455800000,309700000,883728,710608,00:07:57,00:00:00
|
||||||
|
1788527253,15:07:33,15:07:33,7,84240,17432,cap-wifi3,-63,455800000,309700000,883728,710608,00:07:59,00:00:00
|
||||||
|
1788527255,15:07:35,15:07:35,9,74928,18888,cap-wifi3,-62,455800000,412900000,33632,15216,00:08:01,00:00:00
|
||||||
|
1788527257,15:07:37,15:07:37,12,79856,17376,cap-wifi3,-63,455800000,412900000,859640,23024,00:08:03,00:00:00
|
||||||
|
1788527259,15:07:39,15:07:39,19,91680,31720,cap-wifi3,-62,455800000,412900000,1005448,689888,00:08:06,00:00:00
|
||||||
|
1788527261,15:07:41,15:07:41,7,82760,21192,cap-wifi3,-62,455800000,412900000,1005448,689888,00:08:08,00:00:00
|
||||||
|
1788527263,15:07:43,15:07:44,6,91960,31680,cap-wifi3,-63,516200000,412900000,393768,296624,00:08:10,00:00:00
|
||||||
|
1788527266,15:07:46,15:07:46,12,986832,256184,cap-wifi3,-63,516200000,412900000,558880,414936,00:08:12,00:00:00
|
||||||
|
1788527268,15:07:48,15:07:48,8,204344,305984,cap-wifi3,-63,516200000,412900000,558880,414936,00:08:14,00:00:00
|
||||||
|
1788527270,15:07:50,15:07:50,21,90376,25984,cap-wifi3,-63,455800000,344100000,423096,346496,00:08:16,00:00:00
|
||||||
|
1788527272,15:07:52,15:07:52,9,106024,28632,cap-wifi3,-63,455800000,412900000,549712,433200,00:08:18,00:00:00
|
||||||
|
1788527274,15:07:54,15:07:54,8,86752,24568,cap-wifi3,-63,455800000,412900000,549712,433200,00:08:20,00:00:00
|
||||||
|
1788527276,15:07:56,15:07:56,8,68568,17040,cap-wifi3,-63,455800000,344100000,391568,303576,00:08:22,00:00:00
|
||||||
|
1788527278,15:07:58,15:07:58,7,104792,53200,cap-wifi3,-62,455800000,344100000,715856,336320,00:08:24,00:00:00
|
||||||
|
1788527280,15:08:00,15:08:00,17,304368,102592,cap-wifi3,-62,455800000,344100000,715856,336320,00:08:26,00:00:00
|
||||||
|
1788527282,15:08:02,15:08:02,10,74432,14824,cap-wifi3,-62,455800000,344100000,1540504,579064,00:08:28,00:00:00
|
||||||
|
1788527284,15:08:04,15:08:04,15,84656,54904,cap-wifi3,-62,455800000,412900000,241664,55784,00:08:30,00:00:00
|
||||||
|
1788527286,15:08:06,15:08:06,3,157752,48776,cap-wifi3,-62,455800000,412900000,241664,55784,00:08:32,00:00:00
|
||||||
|
1788527288,15:08:08,15:08:08,6,93880,28544,cap-wifi3,-61,455800000,344100000,662200,323008,00:08:34,00:00:00
|
||||||
|
1788527290,15:08:10,15:08:10,5,92704,21680,cap-wifi3,-62,455800000,412900000,1547016,993208,00:08:36,00:00:00
|
||||||
|
1788527292,15:08:12,15:08:12,4,1096952,456376,cap-wifi3,-62,455800000,412900000,1547016,993208,00:08:38,00:00:00
|
||||||
|
1788527294,15:08:14,15:08:14,5,96296,34168,cap-wifi3,-62,455800000,412900000,658464,380200,00:08:40,00:00:00
|
||||||
|
1788527296,15:08:16,15:08:16,4,323536,108008,cap-wifi3,-62,516200000,412900000,1653400,1083248,00:08:42,00:00:00
|
||||||
|
1788527298,15:08:18,15:08:18,5,106776,31872,cap-wifi3,-62,516200000,412900000,1653400,1083248,00:08:44,00:00:00
|
||||||
|
1788527300,15:08:20,15:08:20,5,180440,70648,cap-wifi3,-62,516200000,412900000,601784,323352,00:08:46,00:00:00
|
||||||
|
1788527302,15:08:22,15:08:22,5,258520,122376,cap-wifi3,-63,516200000,344100000,1414784,982048,00:08:48,00:00:00
|
||||||
|
1788527304,15:08:24,15:08:24,8,484544,159104,cap-wifi3,-63,516200000,344100000,1414784,982048,00:08:50,00:00:00
|
||||||
|
1788527306,15:08:26,15:08:26,5,85832,21400,cap-wifi3,-62,516200000,412900000,594728,332184,00:08:52,00:00:00
|
||||||
|
1788527308,15:08:28,15:08:28,7,86968,23984,cap-wifi3,-62,455800000,412900000,1676496,1014016,00:08:54,00:00:00
|
||||||
|
1788527310,15:08:30,15:08:30,4,92848,27216,cap-wifi3,-62,455800000,412900000,1676496,1014016,00:08:57,00:00:00
|
||||||
|
1788527312,15:08:32,15:08:32,13,88112,30272,cap-wifi3,-61,455800000,412900000,862336,707464,00:08:59,00:00:00
|
||||||
|
1788527314,15:08:34,15:08:35,6,97568,38688,cap-wifi3,-62,455800000,412900000,802192,493640,00:09:01,00:00:00
|
||||||
|
1788527317,15:08:37,15:08:37,7,69376,15424,cap-wifi3,-63,455800000,412900000,410264,299608,00:09:03,00:00:00
|
||||||
|
1788527319,15:08:39,15:08:39,7,95672,27872,cap-wifi3,-63,455800000,412900000,410264,299608,00:09:05,00:00:00
|
||||||
|
1788527321,15:08:41,15:08:41,8,81008,14064,cap-wifi3,-62,516200000,412900000,1058008,922664,00:09:07,00:00:00
|
||||||
|
1788527323,15:08:43,15:08:43,8,79480,19824,cap-wifi3,-62,516200000,412900000,396520,170800,00:09:09,00:00:00
|
||||||
|
1788527325,15:08:45,15:08:45,6,83080,28888,cap-wifi3,-62,516200000,412900000,396520,170800,00:09:11,00:00:00
|
||||||
|
1788527327,15:08:47,15:08:47,9,77224,19568,cap-wifi3,-62,516200000,455800000,899408,689584,00:09:13,00:00:00
|
||||||
|
1788527329,15:08:49,15:08:49,7,87976,23544,cap-wifi3,-62,516200000,412900000,44600,15184,00:09:15,00:00:00
|
||||||
|
1788527331,15:08:51,15:08:51,6,99288,43960,cap-wifi3,-62,516200000,412900000,44600,15184,00:09:17,00:00:00
|
||||||
|
1788527333,15:08:53,15:08:53,4,78784,16504,cap-wifi3,-62,516200000,344100000,903552,691192,00:09:19,00:00:00
|
||||||
|
1788527335,15:08:55,15:08:55,8,100664,43024,cap-wifi3,-64,412900000,309700000,69328,39008,00:09:21,00:00:00
|
||||||
|
1788527337,15:08:57,15:08:57,6,75392,68792,cap-wifi3,-64,412900000,309700000,69328,39008,00:09:23,00:00:00
|
||||||
|
1788527339,15:08:59,15:08:59,18,84648,23296,cap-wifi3,-63,344100000,309700000,1149696,710336,00:09:25,00:00:00
|
||||||
|
1788527341,15:09:01,15:09:01,5,146608,49608,cap-wifi3,-62,412900000,344100000,2232624,903848,00:09:27,00:00:00
|
||||||
|
1788527343,15:09:03,15:09:03,6,271512,52760,cap-wifi3,-62,412900000,344100000,2232624,903848,00:09:29,00:00:00
|
||||||
|
1788527345,15:09:05,15:09:05,6,400696,3660640,cap-wifi3,-61,344100000,412900000,756464,448360,00:09:31,00:00:00
|
||||||
|
1788527347,15:09:07,15:09:07,6,131696,25096,cap-wifi3,-63,344100000,344100000,1392608,910264,00:09:33,00:00:00
|
||||||
|
1788527349,15:09:09,15:09:09,19,213416,92736,cap-wifi3,-63,344100000,344100000,1392608,910264,00:09:35,00:00:00
|
||||||
|
1788527351,15:09:11,15:09:11,6,319104,63216,cap-wifi3,-62,412900000,309700000,1209392,477456,00:09:37,00:00:00
|
||||||
|
1788527353,15:09:13,15:09:13,8,153896,48144,cap-wifi3,-62,412900000,344100000,1506680,898696,00:09:39,00:00:00
|
||||||
|
1788527355,15:09:15,15:09:15,5,190952,58360,cap-wifi3,-62,412900000,344100000,1506680,898696,00:09:41,00:00:00
|
||||||
|
1788527357,15:09:17,15:09:17,7,232928,149512,cap-wifi3,-64,412900000,344100000,1033704,613072,00:09:44,00:00:00
|
||||||
|
1788527359,15:09:19,15:09:19,19,108000,26560,cap-wifi3,-64,309700000,344100000,1108648,735944,00:09:46,00:00:00
|
||||||
|
1788527361,15:09:21,15:09:22,10,2754208,213840,cap-wifi3,-64,309700000,344100000,1108648,735944,00:09:48,00:00:00
|
||||||
|
1788527363,15:09:23,15:09:24,7,314040,73608,cap-wifi3,-63,344100000,275300000,981328,613912,00:09:50,00:00:00
|
||||||
|
1788527366,15:09:26,15:09:26,5,485488,100744,cap-wifi3,-58,309700000,309700000,1262008,690008,00:09:52,00:00:00
|
||||||
|
1788527368,15:09:28,15:09:28,6,123816,28072,cap-wifi3,-58,309700000,309700000,1262008,690008,00:09:54,00:00:00
|
||||||
|
1788527370,15:09:30,15:09:30,19,124944,35616,cap-wifi3,-63,309700000,206500000,260104,31680,00:09:56,00:00:00
|
||||||
|
1788527372,15:09:32,15:09:32,5,171352,44496,cap-wifi3,-61,344100000,309700000,1238528,574776,00:09:58,00:00:00
|
||||||
|
1788527374,15:09:34,15:09:34,6,166240,43784,cap-wifi3,-64,344100000,344100000,531000,227904,00:10:00,00:00:00
|
||||||
|
1788527376,15:09:36,15:09:36,6,150760,49464,cap-wifi3,-64,344100000,344100000,531000,227904,00:10:02,00:00:00
|
||||||
|
1788527378,15:09:38,15:09:38,10,185768,47120,cap-wifi3,-64,344100000,309700000,929200,634112,00:10:04,00:00:00
|
||||||
|
1788527380,15:09:40,15:09:40,18,190032,49512,cap-wifi3,-64,344100000,309700000,666528,183736,00:10:06,00:00:00
|
||||||
|
1788527382,15:09:42,15:09:42,8,97808,22304,cap-wifi3,-64,344100000,309700000,666528,183736,00:10:08,00:00:00
|
||||||
|
1788527384,15:09:44,15:09:44,8,148400,53672,cap-wifi3,-63,412900000,344100000,981232,603784,00:10:10,00:00:00
|
||||||
|
1788527386,15:09:46,15:09:46,2,97568,37160,cap-wifi3,-62,412900000,309700000,225464,141192,00:10:12,00:00:00
|
||||||
|
1788527388,15:09:48,15:09:48,4,76456,19280,cap-wifi3,-62,412900000,309700000,225464,141192,00:10:14,00:00:00
|
||||||
|
1788527390,15:09:50,15:09:50,17,74488,14496,cap-wifi3,-63,344100000,275300000,731840,568104,00:10:16,00:00:00
|
||||||
|
1788527392,15:09:52,15:09:52,9,81688,114120,cap-wifi3,-64,412900000,309700000,217144,287216,00:10:18,00:00:00
|
||||||
|
1788527394,15:09:54,15:09:54,11,139472,94312,cap-wifi3,-64,412900000,309700000,217144,287216,00:10:20,00:00:00
|
||||||
|
1788527396,15:09:56,15:09:56,8,99376,34800,cap-wifi3,-63,412900000,344100000,522856,417128,00:10:22,00:00:00
|
||||||
|
1788527398,15:09:58,15:09:58,7,95624,20936,cap-wifi3,-64,412900000,344100000,1177520,673088,00:10:24,00:00:00
|
||||||
|
1788527400,15:10:00,15:10:00,11,75688,17080,cap-wifi3,-64,412900000,344100000,1177520,673088,00:10:26,00:00:00
|
||||||
|
1788527402,15:10:02,15:10:02,8,194448,59984,cap-wifi3,-62,412900000,412900000,1678424,596296,00:10:28,00:00:00
|
||||||
|
1788527404,15:10:04,15:10:04,5,87848,23392,cap-wifi3,-62,412900000,412900000,1099136,718696,00:10:30,00:00:00
|
||||||
|
1788527406,15:10:06,15:10:06,4,102440,31824,cap-wifi3,-62,412900000,412900000,1099136,718696,00:10:32,00:00:00
|
||||||
|
1788527408,15:10:08,15:10:08,7,140352,49312,cap-wifi3,-65,412900000,309700000,944912,595928,00:10:34,00:00:00
|
||||||
|
1788527410,15:10:10,15:10:10,20,81216,18128,cap-wifi3,-64,455800000,344100000,1466688,782256,00:10:36,00:00:00
|
||||||
|
1788527412,15:10:12,15:10:12,8,223088,23248,cap-wifi3,-64,455800000,344100000,1466688,782256,00:10:38,00:00:00
|
||||||
|
1788527414,15:10:14,15:10:14,7,139784,51840,cap-wifi3,-63,412900000,344100000,1572056,878688,00:10:40,00:00:00
|
||||||
|
1788527415,15:10:15,RECONNECT
|
||||||
|
1
docs/messdaten/tp25-runde-dauerping/sampler-macbook.out
Normal file
1
docs/messdaten/tp25-runde-dauerping/sampler-macbook.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-dauerping/sampler-macbook.csv (Takt 2s, Client 56:71:6C:22:E6:A9)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
14:58:12 Verbindung weg -- neuer Versuch in 2 s
|
||||||
|
15:10:15 Verbindung weg -- neuer Versuch in 2 s
|
||||||
130
docs/messdaten/tp25-runde-dauerping/sampler-s22.csv
Normal file
130
docs/messdaten/tp25-runde-dauerping/sampler-s22.csv
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
ts_epoch,ts_local,ros_time,cpu,wan_rx_bps,wan_tx_bps,sta_iface,sta_signal,sta_tx_rate,sta_rx_rate,sta_tx_bps,sta_rx_bps,sta_uptime,sta_last_activity
|
||||||
|
1788527155,15:05:55,15:05:55,26,215104,164208,cap-wifi3,-50,6000000,6000000,0,0,00:00:50,00:00:18.020
|
||||||
|
1788527157,15:05:57,15:05:57,10,230144,103104,cap-wifi3,-49,309700000,344100000,63424,67648,00:00:52,00:00:00
|
||||||
|
1788527159,15:05:59,15:05:59,27,997352,175928,cap-wifi3,-49,309700000,344100000,63424,67648,00:00:55,00:00:00
|
||||||
|
1788527161,15:06:01,15:06:01,5,496624,179032,cap-wifi3,-49,309700000,344100000,2800,2960,00:00:57,00:00:00
|
||||||
|
1788527163,15:06:03,15:06:03,7,112272,25664,cap-wifi3,-49,309700000,344100000,2800,2960,00:00:59,00:00:00
|
||||||
|
1788527165,15:06:05,15:06:06,6,353280,101384,cap-wifi3,-49,309700000,344100000,2800,2960,00:01:01,00:00:00
|
||||||
|
1788527168,15:06:08,15:06:08,6,137256,37360,cap-wifi3,-53,309700000,344100000,5472,4672,00:01:03,00:00:00
|
||||||
|
1788527170,15:06:10,15:06:10,18,373144,1851168,cap-wifi3,-61,275300000,206500000,2800,2960,00:01:05,00:00:00
|
||||||
|
1788527172,15:06:12,15:06:12,2,79504,15688,cap-wifi3,-61,275300000,206500000,2800,2960,00:01:07,00:00:00
|
||||||
|
1788527174,15:06:14,15:06:14,6,215576,3512448,cap-wifi3,-56,154900000,258100000,3592,4872,00:01:09,00:00:00
|
||||||
|
1788527176,15:06:16,15:06:16,4,170440,37144,cap-wifi3,-51,24000000,229400000,2800,10960,00:01:11,00:00:00
|
||||||
|
1788527178,15:06:18,15:06:18,7,187488,1860720,cap-wifi3,-51,24000000,229400000,2800,10960,00:01:13,00:00:00
|
||||||
|
1788527180,15:06:20,15:06:20,21,418584,3563696,cap-wifi3,-52,275300000,258100000,2800,2960,00:01:15,00:00:00
|
||||||
|
1788527182,15:06:22,15:06:22,6,91920,32648,cap-wifi3,-51,275300000,286800000,2800,10960,00:01:17,00:00:00
|
||||||
|
1788527184,15:06:24,15:06:24,8,229720,340576,cap-wifi3,-51,275300000,286800000,2800,10960,00:01:19,00:00:00
|
||||||
|
1788527186,15:06:26,15:06:26,11,324680,63616,cap-wifi3,-52,275300000,286800000,4184,4376,00:01:21,00:00:00
|
||||||
|
1788527188,15:06:28,15:06:28,7,323944,57120,cap-wifi3,-50,275300000,309700000,3360,3552,00:01:23,00:00:00
|
||||||
|
1788527190,15:06:30,15:06:30,7,89536,26840,cap-wifi3,-50,275300000,309700000,3360,3552,00:01:25,00:00:00
|
||||||
|
1788527192,15:06:32,15:06:32,6,88056,17368,cap-wifi3,-50,275300000,344100000,5344,8864,00:01:27,00:00:00
|
||||||
|
1788527194,15:06:34,15:06:34,6,78232,15688,cap-wifi3,-52,275300000,309700000,3248,3328,00:01:29,00:00:00
|
||||||
|
1788527196,15:06:36,15:06:36,6,77824,19488,cap-wifi3,-52,275300000,309700000,3248,3328,00:01:31,00:00:00
|
||||||
|
1788527198,15:06:38,15:06:38,8,91648,29960,cap-wifi3,-58,275300000,344100000,2800,2960,00:01:33,00:00:00
|
||||||
|
1788527200,15:06:40,15:06:40,6,479584,403096,cap-wifi3,-51,275300000,229400000,2800,2960,00:01:35,00:00:00
|
||||||
|
1788527202,15:06:42,15:06:42,5,83504,18032,cap-wifi3,-51,275300000,229400000,2800,2960,00:01:37,00:00:00
|
||||||
|
1788527204,15:06:44,15:06:44,12,94312,27480,cap-wifi3,-54,275300000,258100000,2800,2960,00:01:39,00:00:00
|
||||||
|
1788527206,15:06:46,15:06:46,11,104592,25288,cap-wifi3,-53,275300000,286800000,2800,10960,00:01:41,00:00:00
|
||||||
|
1788527208,15:06:48,15:06:48,9,77672,15824,cap-wifi3,-52,24000000,286800000,2800,2960,00:01:43,00:00:00
|
||||||
|
1788527210,15:06:50,15:06:50,8,130536,65520,cap-wifi3,-52,24000000,286800000,2800,2960,00:01:46,00:00:00
|
||||||
|
1788527212,15:06:52,15:06:52,6,179704,71320,cap-wifi3,-53,6000000,286800000,3360,3552,00:01:48,00:00:00
|
||||||
|
1788527214,15:06:54,15:06:54,7,150376,135320,cap-wifi3,-51,275300000,309700000,2800,2960,00:01:50,00:00:00
|
||||||
|
1788527216,15:06:56,15:06:57,7,180576,117008,cap-wifi3,-51,275300000,309700000,2800,2960,00:01:52,00:00:00
|
||||||
|
1788527219,15:06:59,15:06:59,9,81080,17128,cap-wifi3,-52,275300000,309700000,2800,2960,00:01:54,00:00:00
|
||||||
|
1788527221,15:07:01,15:07:01,6,267072,71760,cap-wifi3,-51,275300000,309700000,2800,2960,00:01:56,00:00:00
|
||||||
|
1788527223,15:07:03,15:07:03,7,229720,79648,cap-wifi3,-51,275300000,309700000,2800,2960,00:01:58,00:00:00
|
||||||
|
1788527225,15:07:05,15:07:05,12,613656,242376,cap-wifi3,-53,275300000,344100000,2800,2960,00:02:00,00:00:00
|
||||||
|
1788527227,15:07:07,15:07:07,9,136256,40080,cap-wifi3,-52,275300000,344100000,3808,3920,00:02:02,00:00:00
|
||||||
|
1788527229,15:07:09,15:07:09,11,113040,49536,cap-wifi3,-52,275300000,344100000,3808,3920,00:02:04,00:00:00
|
||||||
|
1788527231,15:07:11,15:07:11,7,85432,17976,cap-wifi3,-53,275300000,309700000,2800,2960,00:02:06,00:00:00
|
||||||
|
1788527233,15:07:13,15:07:13,5,81944,26128,cap-wifi3,-56,275300000,286800000,2800,3552,00:02:08,00:00:00
|
||||||
|
1788527235,15:07:15,15:07:15,5,954264,397144,cap-wifi3,-56,275300000,286800000,2800,3552,00:02:10,00:00:00
|
||||||
|
1788527237,15:07:17,15:07:17,12,3430312,274976,cap-wifi3,-50,275300000,344100000,11168,29128,00:02:12,00:00:00
|
||||||
|
1788527239,15:07:19,15:07:19,21,5774264,240088,cap-wifi3,-50,275300000,344100000,3360,3552,00:02:14,00:00:00
|
||||||
|
1788527241,15:07:21,15:07:21,7,419688,124632,cap-wifi3,-50,275300000,344100000,3360,3552,00:02:16,00:00:00
|
||||||
|
1788527243,15:07:23,15:07:23,6,146872,47152,cap-wifi3,-50,275300000,344100000,2800,2960,00:02:18,00:00:00
|
||||||
|
1788527245,15:07:25,15:07:25,6,469520,144920,cap-wifi3,-50,275300000,344100000,2800,2960,00:02:20,00:00:00
|
||||||
|
1788527247,15:07:27,15:07:27,8,87480,21512,cap-wifi3,-50,275300000,344100000,2800,2960,00:02:22,00:00:00
|
||||||
|
1788527249,15:07:29,15:07:29,20,89592,20496,cap-wifi3,-50,275300000,344100000,3360,3552,00:02:24,00:00:00
|
||||||
|
1788527251,15:07:31,15:07:31,9,77304,18144,cap-wifi3,-50,275300000,344100000,2800,2960,00:02:26,00:00:00
|
||||||
|
1788527253,15:07:33,15:07:33,7,84240,17432,cap-wifi3,-50,275300000,344100000,2800,2960,00:02:28,00:00:00
|
||||||
|
1788527255,15:07:35,15:07:35,9,74928,18888,cap-wifi3,-51,275300000,344100000,2240,2368,00:02:30,00:00:00
|
||||||
|
1788527257,15:07:37,15:07:37,12,79856,17376,cap-wifi3,-51,275300000,344100000,2800,2960,00:02:33,00:00:00
|
||||||
|
1788527259,15:07:39,15:07:39,19,91680,31720,cap-wifi3,-52,275300000,344100000,3808,3920,00:02:35,00:00:00
|
||||||
|
1788527261,15:07:41,15:07:41,7,82760,21192,cap-wifi3,-52,275300000,344100000,3808,3920,00:02:37,00:00:00
|
||||||
|
1788527263,15:07:43,15:07:44,6,91960,31680,cap-wifi3,-51,309700000,344100000,4912,4080,00:02:39,00:00:00
|
||||||
|
1788527266,15:07:46,15:07:46,12,986832,256184,cap-wifi3,-51,309700000,344100000,3360,3552,00:02:41,00:00:00
|
||||||
|
1788527268,15:07:48,15:07:48,8,204344,305984,cap-wifi3,-51,309700000,344100000,3360,3552,00:02:43,00:00:00
|
||||||
|
1788527270,15:07:50,15:07:50,21,90376,25984,cap-wifi3,-50,309700000,412900000,2800,2960,00:02:45,00:00:00
|
||||||
|
1788527272,15:07:52,15:07:52,9,106024,28632,cap-wifi3,-51,309700000,344100000,3360,3552,00:02:47,00:00:00
|
||||||
|
1788527274,15:07:54,15:07:54,8,86752,24568,cap-wifi3,-51,309700000,344100000,3360,3552,00:02:49,00:00:00
|
||||||
|
1788527276,15:07:56,15:07:56,8,68568,17040,cap-wifi3,-51,309700000,412900000,2800,2960,00:02:51,00:00:00
|
||||||
|
1788527278,15:07:58,15:07:58,7,104792,53200,cap-wifi3,-48,206500000,344100000,3360,3552,00:02:53,00:00:00
|
||||||
|
1788527280,15:08:00,15:08:00,17,304368,102592,cap-wifi3,-48,206500000,344100000,3360,3552,00:02:55,00:00:00
|
||||||
|
1788527282,15:08:02,15:08:02,10,74432,14824,cap-wifi3,-49,206500000,344100000,2800,2960,00:02:57,00:00:00
|
||||||
|
1788527284,15:08:04,15:08:04,15,84656,54904,cap-wifi3,-48,206500000,344100000,2800,2960,00:02:59,00:00:00
|
||||||
|
1788527286,15:08:06,15:08:06,3,157752,48776,cap-wifi3,-48,206500000,344100000,2800,2960,00:03:01,00:00:00
|
||||||
|
1788527288,15:08:08,15:08:08,6,93880,28544,cap-wifi3,-48,206500000,344100000,3360,3552,00:03:03,00:00:00
|
||||||
|
1788527290,15:08:10,15:08:10,5,92704,21680,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:05,00:00:00
|
||||||
|
1788527292,15:08:12,15:08:12,4,1096952,456376,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:07,00:00:00
|
||||||
|
1788527294,15:08:14,15:08:14,5,96296,34168,cap-wifi3,-48,206500000,344100000,2800,2960,00:03:09,00:00:00
|
||||||
|
1788527296,15:08:16,15:08:16,4,323536,108008,cap-wifi3,-48,206500000,344100000,4184,4376,00:03:11,00:00:00
|
||||||
|
1788527298,15:08:18,15:08:18,5,106776,31872,cap-wifi3,-48,206500000,344100000,4184,4376,00:03:13,00:00:00
|
||||||
|
1788527300,15:08:20,15:08:20,5,180440,70648,cap-wifi3,-49,206500000,344100000,3360,3552,00:03:15,00:00:00
|
||||||
|
1788527302,15:08:22,15:08:22,5,258520,122376,cap-wifi3,-49,206500000,344100000,2800,10960,00:03:17,00:00:00
|
||||||
|
1788527304,15:08:24,15:08:24,8,484544,159104,cap-wifi3,-49,206500000,344100000,2800,10960,00:03:19,00:00:00
|
||||||
|
1788527306,15:08:26,15:08:26,5,85832,21400,cap-wifi3,-48,206500000,344100000,3360,2960,00:03:21,00:00:00
|
||||||
|
1788527308,15:08:28,15:08:28,7,86968,23984,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:24,00:00:00
|
||||||
|
1788527310,15:08:30,15:08:30,4,92848,27216,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:26,00:00:00
|
||||||
|
1788527312,15:08:32,15:08:32,13,88112,30272,cap-wifi3,-49,206500000,344100000,3360,3552,00:03:28,00:00:00
|
||||||
|
1788527314,15:08:34,15:08:35,6,97568,38688,cap-wifi3,-50,206500000,344100000,2800,2960,00:03:30,00:00:00
|
||||||
|
1788527317,15:08:37,15:08:37,7,69376,15424,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:32,00:00:00
|
||||||
|
1788527319,15:08:39,15:08:39,7,95672,27872,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:34,00:00:00
|
||||||
|
1788527321,15:08:41,15:08:41,8,81008,14064,cap-wifi3,-49,206500000,344100000,3360,3552,00:03:36,00:00:00
|
||||||
|
1788527323,15:08:43,15:08:43,8,79480,19824,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:38,00:00:00
|
||||||
|
1788527325,15:08:45,15:08:45,6,83080,28888,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:40,00:00:00
|
||||||
|
1788527327,15:08:47,15:08:47,9,77224,19568,cap-wifi3,-49,206500000,309700000,3360,3552,00:03:42,00:00:00
|
||||||
|
1788527329,15:08:49,15:08:49,7,87976,23544,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:44,00:00:00
|
||||||
|
1788527331,15:08:51,15:08:51,6,99288,43960,cap-wifi3,-49,206500000,344100000,2800,2960,00:03:46,00:00:00
|
||||||
|
1788527333,15:08:53,15:08:53,7,78784,16504,cap-wifi3,-45,206500000,309700000,2800,2960,00:03:48,00:00:00
|
||||||
|
1788527335,15:08:55,15:08:55,8,100664,43024,cap-wifi3,-60,206500000,309700000,2800,2960,00:03:50,00:00:00
|
||||||
|
1788527337,15:08:57,15:08:57,6,75392,68792,cap-wifi3,-60,206500000,309700000,2800,2960,00:03:52,00:00:00
|
||||||
|
1788527339,15:08:59,15:08:59,18,84648,23296,cap-wifi3,-58,206500000,309700000,2800,2960,00:03:54,00:00:00
|
||||||
|
1788527341,15:09:01,15:09:01,5,146608,49608,cap-wifi3,-54,6000000,6000000,3360,3552,00:03:56,00:00:00
|
||||||
|
1788527343,15:09:03,15:09:03,6,271512,52760,cap-wifi3,-54,6000000,6000000,3360,3552,00:03:58,00:00:00
|
||||||
|
1788527345,15:09:05,15:09:05,6,400696,3660640,cap-wifi3,-56,275300000,344100000,2800,2960,00:04:00,00:00:00
|
||||||
|
1788527347,15:09:07,15:09:07,6,131696,25096,cap-wifi3,-54,275300000,344100000,3360,3552,00:04:02,00:00:00
|
||||||
|
1788527349,15:09:09,15:09:09,19,174312,46976,cap-wifi3,-54,275300000,344100000,3360,3552,00:04:04,00:00:00
|
||||||
|
1788527351,15:09:11,15:09:11,6,359504,63184,cap-wifi3,-54,275300000,309700000,2800,2960,00:04:06,00:00:00
|
||||||
|
1788527353,15:09:13,15:09:13,8,181920,63704,cap-wifi3,-58,24000000,344100000,2800,4896,00:04:08,00:00:00
|
||||||
|
1788527355,15:09:15,15:09:15,5,306368,67704,cap-wifi3,-58,24000000,344100000,2800,4896,00:04:10,00:00:00
|
||||||
|
1788527357,15:09:17,15:09:17,7,306688,60896,cap-wifi3,-57,275300000,309700000,3656,3520,00:04:12,00:00:00
|
||||||
|
1788527359,15:09:19,15:09:19,19,310240,63104,cap-wifi3,-57,154900000,309700000,4384,6048,00:04:14,00:00:00
|
||||||
|
1788527361,15:09:21,15:09:21,10,228704,92160,cap-wifi3,-57,154900000,309700000,4384,6048,00:04:16,00:00:00
|
||||||
|
1788527363,15:09:23,15:09:23,7,292296,160184,cap-wifi3,-61,154900000,309700000,10568,37976,00:04:18,00:00:00
|
||||||
|
1788527365,15:09:25,15:09:25,5,695128,395648,cap-wifi3,-56,154900000,309700000,3360,3552,00:04:20,00:00:00
|
||||||
|
1788527367,15:09:27,15:09:27,6,123816,28072,cap-wifi3,-56,154900000,309700000,3360,3552,00:04:22,00:00:00
|
||||||
|
1788527369,15:09:29,15:09:29,19,124944,35616,cap-wifi3,-57,154900000,344100000,2800,2960,00:04:24,00:00:00
|
||||||
|
1788527371,15:09:31,15:09:31,5,171352,44496,cap-wifi3,-57,154900000,275300000,4040,6584,00:04:26,00:00:00
|
||||||
|
1788527373,15:09:33,15:09:33,6,166240,43784,cap-wifi3,-57,154900000,275300000,4040,6584,00:04:28,00:00:00
|
||||||
|
1788527375,15:09:35,15:09:35,6,150760,49464,cap-wifi3,-56,154900000,309700000,2800,2960,00:04:31,00:00:00
|
||||||
|
1788527377,15:09:37,15:09:37,10,185768,47120,cap-wifi3,-57,24000000,309700000,2800,2960,00:04:33,00:00:00
|
||||||
|
1788527379,15:09:39,15:09:39,18,190032,49512,cap-wifi3,-57,24000000,309700000,2800,2960,00:04:35,00:00:00
|
||||||
|
1788527381,15:09:41,15:09:41,8,97808,22304,cap-wifi3,-57,154900000,344100000,2800,2960,00:04:37,00:00:00
|
||||||
|
1788527383,15:09:43,15:09:43,8,148400,53672,cap-wifi3,-57,154900000,344100000,2800,2960,00:04:39,00:00:00
|
||||||
|
1788527386,15:09:46,15:09:46,2,97568,37160,cap-wifi3,-57,154900000,344100000,2800,2960,00:04:41,00:00:00
|
||||||
|
1788527387,15:09:47,15:09:48,4,76456,19280,cap-wifi3,-57,154900000,309700000,2800,3552,00:04:43,00:00:00
|
||||||
|
1788527389,15:09:49,15:09:50,17,74488,14496,cap-wifi3,-58,6000000,309700000,2800,2960,00:04:45,00:00:00
|
||||||
|
1788527392,15:09:52,15:09:52,4,81688,114120,cap-wifi3,-58,6000000,309700000,2800,2960,00:04:47,00:00:00
|
||||||
|
1788527394,15:09:54,15:09:54,8,139472,94312,cap-wifi3,-57,154900000,309700000,2800,2960,00:04:49,00:00:00
|
||||||
|
1788527396,15:09:56,15:09:56,4,99376,34800,cap-wifi3,-57,154900000,309700000,2800,2960,00:04:51,00:00:00
|
||||||
|
1788527398,15:09:58,15:09:58,5,95624,20936,cap-wifi3,-57,154900000,309700000,2800,2960,00:04:53,00:00:00
|
||||||
|
1788527400,15:10:00,15:10:00,19,75688,17080,cap-wifi3,-57,154900000,344100000,2800,2960,00:04:55,00:00:00
|
||||||
|
1788527402,15:10:02,15:10:02,12,194448,59984,cap-wifi3,-56,154900000,344100000,2800,2960,00:04:57,00:00:00
|
||||||
|
1788527404,15:10:04,15:10:04,10,87848,23392,cap-wifi3,-58,154900000,344100000,2800,2960,00:04:59,00:00:00
|
||||||
|
1788527406,15:10:06,15:10:06,9,102440,31824,cap-wifi3,-58,154900000,344100000,2800,2960,00:05:01,00:00:00
|
||||||
|
1788527408,15:10:08,15:10:08,8,140352,49312,cap-wifi3,-56,154900000,309700000,2800,2960,00:05:03,00:00:00
|
||||||
|
1788527410,15:10:10,15:10:10,25,88352,30376,cap-wifi3,-56,154900000,309700000,2800,2960,00:05:05,00:00:00
|
||||||
|
1788527412,15:10:12,15:10:12,7,124328,46808,cap-wifi3,-56,154900000,309700000,2800,2960,00:05:07,00:00:00
|
||||||
|
1788527414,15:10:14,15:10:14,8,84784,23040,cap-wifi3,-57,154900000,344100000,2800,2960,00:05:09,00:00:00
|
||||||
|
1788527415,15:10:15,RECONNECT
|
||||||
|
1
docs/messdaten/tp25-runde-dauerping/sampler-s22.out
Normal file
1
docs/messdaten/tp25-runde-dauerping/sampler-s22.out
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Sampler laeuft -> /Users/christophemsenhuber/dev/bf-infra/docs/messdaten/tp25-runde-dauerping/sampler-s22.csv (Takt 2s, Client 2A:0D:B0:5D:AD:A7)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
14:58:12 Verbindung weg -- neuer Versuch in 2 s
|
||||||
|
15:10:15 Verbindung weg -- neuer Versuch in 2 s
|
||||||
0
docs/messdaten/tp25-runde-dauerping/stop.flag
Normal file
0
docs/messdaten/tp25-runde-dauerping/stop.flag
Normal file
1280
docs/messdaten/tp25-runde-dauerping/walk-ping-ipad.csv
Normal file
1280
docs/messdaten/tp25-runde-dauerping/walk-ping-ipad.csv
Normal file
File diff suppressed because it is too large
Load Diff
1299
docs/messdaten/tp25-runde-dauerping/walk-ping-iphone.csv
Normal file
1299
docs/messdaten/tp25-runde-dauerping/walk-ping-iphone.csv
Normal file
File diff suppressed because it is too large
Load Diff
1304
docs/messdaten/tp25-runde-dauerping/walk-ping-macbook.csv
Normal file
1304
docs/messdaten/tp25-runde-dauerping/walk-ping-macbook.csv
Normal file
File diff suppressed because it is too large
Load Diff
1302
docs/messdaten/tp25-runde-dauerping/walk-ping-s22.csv
Normal file
1302
docs/messdaten/tp25-runde-dauerping/walk-ping-s22.csv
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -63,6 +63,11 @@ teuerste Eigenschaft der Datei zerstört, um ihr billigstes Problem zu lösen. B
|
|||||||
| [TP28](#tp28) | WLAN über CAPsMAN auf `crs326-core` | ↩️ **wieder geöffnet 2026-08-30** (ap3) — Volltext in [teilprojekte.md](teilprojekte.md#tp28) |
|
| [TP28](#tp28) | WLAN über CAPsMAN auf `crs326-core` | ↩️ **wieder geöffnet 2026-08-30** (ap3) — Volltext in [teilprojekte.md](teilprojekte.md#tp28) |
|
||||||
| [TP-R](#tp-r) | Router-Capabilities-Sweep | FERTIG (docs/research/routeros-capabilities.md) |
|
| [TP-R](#tp-r) | Router-Capabilities-Sweep | FERTIG (docs/research/routeros-capabilities.md) |
|
||||||
| [TP39](#tp39) | LXC-Hygiene pve: Resolver-Altlasten, `networking.service`, DB-Reverse-Lookup | ✅ ABGESCHLOSSEN (2026-09-01) — pve+NAS-Sweep grün |
|
| [TP39](#tp39) | LXC-Hygiene pve: Resolver-Altlasten, `networking.service`, DB-Reverse-Lookup | ✅ ABGESCHLOSSEN (2026-09-01) — pve+NAS-Sweep grün |
|
||||||
|
| [TP34](#tp34) | Caddyfile via Nautobot/GC (generische Reverse-Proxy-Schnittstelle) | ❌ VERWORFEN (2026-09-08) — Caddyfile bleibt manuell |
|
||||||
|
| [TP44](#tp44) | Cloudflare-Estate als Dokumentations-SSoT in Nautobot | ✅ DURCH (2026-09-10) |
|
||||||
|
| [TP41](#tp41) | CF-Access-Reparatur + Zero-Trust-Konsolidierung | ✅ ABGESCHLOSSEN (2026-09-10) — Gelis WARP via Android eingerichtet, letzter Blocker weg |
|
||||||
|
| [TP43](#tp43) | dw-Standort-DNS-Uplink (site-spezifische Overrides) | ♻️ REOPENED (2026-09-11) — Volltext zurück in teilprojekte.md; interne BF-Namen über WG am dw offen |
|
||||||
|
| [TP42](#tp42) | Namens-Vereinheitlichung: ein interner Suffix bf.emsenhuber.net | ✅ DURCH (2026-09-02) — `.bf.lan` raus, `.16` abgebaut, Compliance 140/140 |
|
||||||
|
|
||||||
Dazu am Ende: **[TP25 (Langfassung)](#tp25-lang)** — die Messchronik und die Funde A–E im Volltext.
|
Dazu am Ende: **[TP25 (Langfassung)](#tp25-lang)** — die Messchronik und die Funde A–E im Volltext.
|
||||||
TP25 selbst ist **nicht** abgeschlossen und steht weiter in `teilprojekte.md`; nur seine 394 Zeilen
|
TP25 selbst ist **nicht** abgeschlossen und steht weiter in `teilprojekte.md`; nur seine 394 Zeilen
|
||||||
@@ -1777,3 +1782,198 @@ gemessen hat. Beim Beheben kam eine zweite, unabhängige Altlast zum Vorschein.
|
|||||||
- **Nicht** TP31/TP34 (Caddy) und nicht TP11 (Monitoring) — obwohl gilt: ein Monitoring, das
|
- **Nicht** TP31/TP34 (Caddy) und nicht TP11 (Monitoring) — obwohl gilt: ein Monitoring, das
|
||||||
DB-Verbindungszeiten kennt, hätte diesen Fall Monate früher gezeigt.
|
DB-Verbindungszeiten kennt, hätte diesen Fall Monate früher gezeigt.
|
||||||
- **Nicht** TP40: der HA-Recorder-Wildwuchs (Punkt 3) ist HA-seitig und ein eigenes TP.
|
- **Nicht** TP40: der HA-Recorder-Wildwuchs (Punkt 3) ist HA-seitig und ein eigenes TP.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### TP34 — Caddyfile via Nautobot/GC (generische Reverse-Proxy-Schnittstelle) ▸ ❌ **VERWORFEN (2026-09-08)**
|
||||||
|
> 🔴 **Nachtrag 2026-09-08 (verworfen):** Der Ansatz „Caddyfile via Golden Config rendern" ist
|
||||||
|
> zurückgezogen (Commits `d63dc7b`, `bca8f6e`; Gitea-Issue #24 → `status:dropped`, closed). Der
|
||||||
|
> `420.teilzeitladen.xyz`-Block wurde wieder aus dem GC-gerenderten Caddyfile entfernt; **der
|
||||||
|
> Caddyfile bleibt manuell** in `caddy/Caddyfile`. Gründe: kein zweites SSoT-Modell für Proxy-Routen
|
||||||
|
> nötig, TP37 deckt die Dienst-Doku als `ipam.Service` bereits ab, und der einzige akute Anwendungsfall
|
||||||
|
> ließ sich mit einem manuellen Block lösen. Text unten unverändert als Konzept-Historie.
|
||||||
|
|
||||||
|
> **Konzept + Architektur-Skizze:** [`docs/handoff-20260827-inventur-und-architektur.md`](handoff-20260827-inventur-und-architektur.md) — bitte dort weiterlesen (Architektur-Abschnitt).
|
||||||
|
- **Scope:** Reverse-Proxy-Hosts werden im **Nautobot-Datenmodell** gepflegt (Site, Host, Backend-Spec). **Golden Config** rendert den Caddyfile aus den Daten, Diff-Check gegen die Live-Version auf `caddy-bf1`, Compliance-Checks + Transaktionen identisch zum bestehenden Workflow für Router-Configs.
|
||||||
|
- **Auslöser (Inventur 2026-08-27):** der Caddyfile (`bf-infra/caddy/Caddyfile`) ist heute **manuell gepflegt** — kein Drift-Check, keine Transaktionen, andere Projekte können keine Sites anlegen ohne Christoph. Lösung: **Nautobot/GC**, kein Caddy-API-Sidecar (kein zweites SSoT).
|
||||||
|
- **Erster Anwendungsfall:** `terpene.teilzeitladen.xyz → http://10.40.20.167:8890` (Mac-mini-Backend, dort PostgREST + sync + web via lokalem Caddy `:8890`). Vor TP34: manueller Quick-Win-Caddy-Block.
|
||||||
|
- **Abh.:** TP22 (GC-Pipeline) ✅ · TP23 (DNS/Caddy-Voraussetzungen) ✅ Stufe 1 · 🔴 **NEU 2026-09-06: TP37 Punkt 1+2** — Caddy rendert *Config aus Daten*, die Backend-Dienste müssen als `ipam.Service` in Nautobot existieren, sonst gibt es nichts zu rendern. **TP37 vor TP34.**
|
||||||
|
- **Datenmodell (entschieden 2026-09-06):** native Modelle — Backend = `ipam.Service`, Proxy-Route = Relationship/CustomField Frontend↔Backend, GC-Job rendert den Caddyfile. Keine eigene App (konsistent zu TP44).
|
||||||
|
- **Status:** 🔜 Stub; wartet auf TP37 P1+2. Quick-Win `terpene.teilzeitladen.xyz → 10.40.20.167:8890` als manueller Caddy-Block ist **Schritt 0** des Runbooks. Plan: [`handoff-20260906-tp37-34-36-plan.md`](handoff-20260906-tp37-34-36-plan.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<a id="tp44"></a>
|
||||||
|
### TP44 — Cloudflare-Estate als Dokumentations-SSoT in Nautobot ▸ ✅ **DURCH (2026-09-10): Etappe 1 + Etappe 2 (Teil A IPAM-Prefixe, Teil B Live-Sync als täglicher Nautobot-Job)**
|
||||||
|
> **Entscheid (User, 2026-09-02):** die volle CF-Estate (Tunnel + WARP-Connector + VPC-Service +
|
||||||
|
> Hyperdrive + Workers + alle Access-Apps) als strukturierte, abfragbare SSoT in Nautobot abbilden —
|
||||||
|
> **etappenweise: manuell jetzt, Sync später.** Nautobot kann CF nicht steuern → „SSoT" heißt
|
||||||
|
> **Dokumentations-SSoT; CF bleibt operativer Master.** Detail-Handoff:
|
||||||
|
> [`handoff-20260902-cf-estate-nautobot-ssot.md`](handoff-20260902-cf-estate-nautobot-ssot.md).
|
||||||
|
|
||||||
|
- **Warum eigenes TP, nicht TP36:** TP36 ist eng auf `teilprojekte.md`-Live-Daten geschnitten; gleiche
|
||||||
|
Idee (Nautobot als Doku-SSoT), andere Dimension. TP44 = nächste freie Nummer nach TP43.
|
||||||
|
- **Modell (native `nautobot.cloud`, Nautobot 3.1.7 — keine eigene App):** `Manufacturer "Cloudflare"` →
|
||||||
|
`CloudAccount` (`393c051f…`) → `CloudResourceType ×6` → `CloudNetwork ×4` (`bf-public`/`fm`/`terpene-db`/
|
||||||
|
`bf-core-mesh`) + `CloudService ×14`; Service↔Tunnel über das Through-Modell
|
||||||
|
`cloud-service-network-assignments` (3 Rows). IDs/Ports/Hostnames/Origins je Objekt in `extra_config`.
|
||||||
|
- **Werkzeug:** `nautobot/tp44-cf-estate-seed.py` — idempotenter Seeder (Re-Run = 26/26 „existiert"),
|
||||||
|
Datenstand inline (Live-CF-Inventur 2026-09-02). Token aus `docs/nas-vlan20/stacks/210-nautobot.yml`.
|
||||||
|
- ✅ **Verifikation 2026-09-06 (live gegen die API):** CloudAccount 1 · ResourceType 6 · CloudNetwork 4 ·
|
||||||
|
CloudService 14 — unverändert seit 09-02.
|
||||||
|
- 🔴 **Gotchas:** `CloudService.cloud_networks` ist `read_only` (nur Through-Rows schreiben/lesen) ·
|
||||||
|
VPC-Service-API unter `connectivity/directory/services` (PUT, voller Body) · **VPC-Objekte tragen ihren
|
||||||
|
Identifier in `service_id`, NICHT `id`** (kostete im Sync einen Fehl-Match) · ⚠️ **DRIFT-KORREKTUR
|
||||||
|
2026-09-10:** die frühere Zeile „Repo-`CF_API_TOKEN` darf kein Workers-VPC → über den `cloudflare`-MCP"
|
||||||
|
war **falsch**. Live getestet liest der `CF_API_TOKEN` aus `~/.secrets/credentials.env` Hyperdrive **und**
|
||||||
|
VPC **und** Workers (alle 200). Der schmale Token ist `CF_DNS_API_TOKEN` (403). Der `cloudflare`-MCP ist
|
||||||
|
der **remote OAuth-MCP** (kein API-Token) und für einen serverseitigen Job ohnehin nicht nutzbar.
|
||||||
|
- ✅ **Etappe 2 Teil A (2026-09-08): IPAM-Prefixe live.** `10.40.0.0/16` (container, Route `bf-core-mesh`) +
|
||||||
|
`10.101.0.0/24` (network, Route `fm`) angelegt via `nautobot/tp44-ipam-prefixes.py`; der Seeder-Re-Run
|
||||||
|
hat die zwei `cloud-network-prefix-assignments` eingehängt (count=2, Idempotenz-Gegenprobe grün).
|
||||||
|
- ✅ **Etappe 2 Teil B (2026-09-10): Live-Sync als Nautobot-Job — DURCH.** Governance (User 2026-09-09):
|
||||||
|
periodische externe→Nautobot-Syncs leben als **Nautobot-Jobs** in `bf-infra/jobs/`, in Nautobot geplant —
|
||||||
|
**kein per-Task-Container/-Cron.** Runtime `jobs/cf_estate_sync.py` (`CFEstateSync`, ORM, read-only Pull,
|
||||||
|
403-tolerant, Verwaisung = `extra_config.stale` statt Löschen). Token = Nautobot-Secret `cf-api-token`
|
||||||
|
(Provider env → `CF_API_TOKEN` am Worker, in `210-nautobot.yml` verdrahtet). **Schedule: täglich 03:00Z**
|
||||||
|
(`ScheduledJob "CF Estate Sync – taeglich"`). Verifiziert: Dry-Run + Commit-Run sauber, **4 Networks /
|
||||||
|
14 Services, alle Endpoints lesbar, 0 Drift**. Der REST-Einmalproof `nautobot/tp44-cf-sync.py` bleibt als
|
||||||
|
workstation-seitiges Diagnose-Werkzeug liegen (nicht die kanonische Runtime).
|
||||||
|
- 🔴 **Drei load-bearing Funde (Detail-Handoff `handoff-20260910-tp44-teilb-sync-live.md`):**
|
||||||
|
(1) **Wurzel-`__init__.py` im Repo ist Pflicht** — Nautobot lädt Git-Repo-Jobs via
|
||||||
|
`pkgutil.walk_packages([GIT_ROOT])`, das Namespace-Packages (ohne `__init__.py`) NICHT entdeckt; ohne die
|
||||||
|
Datei bleibt `bf_infra.jobs` unsichtbar (Symptom: Sync SUCCESS + „No jobs were registered"). bf-infra ist
|
||||||
|
das erste Git-Repo mit `extras.job`. (2) `register_jobs()` gehört ins **Paket-`__init__`**, nicht nur ins
|
||||||
|
Submodul. (3) `Meta.has_sensitive_variables=False` nötig, sonst verweigert Nautobot das Scheduling.
|
||||||
|
- **Beziehung zu TP37:** TP44 hat **TP37 Punkt 3** (CF-Connectoren) vorweggenommen — dieselbe native-Modell-
|
||||||
|
Linie, die TP37 P1+2 (Substrat/Dienste) fortsetzt.
|
||||||
|
|
||||||
|
### TP41 — CF-Access-Reparatur + Zero-Trust-Konsolidierung ▸ ✅ **ABGESCHLOSSEN (2026-09-10)** — Auth-Kern seit 2026-09-01 komplett; **Gelis WARP-Client am 2026-09-10 via Android erfolgreich eingerichtet** (letzter Blocker weg, User-Bestätigung). Optionale Restpunkte (T6 Rate-Limit, geli-CGNAT-Policy-Cleanup) bewusst nicht gezogen; Keystore-Rotation läuft unter TP30 #7.
|
||||||
|
|
||||||
|
**Auslöser:** CF-Access zwang Christoph **und Geli** täglich zum OTP-Login (soll: 30 Tage); GitHub/Google
|
||||||
|
„funktionieren nicht". Root-Causes: Team-Domain-Rename `bf4`→`emsenhuber` (Provider-Callback veraltet),
|
||||||
|
App-Session 24h, diverse Ordnungs-/Sicherheitslecks. Vollständiger Verlauf + offene Schritte:
|
||||||
|
**[`docs/handoff-20260901-tp41.md`](handoff-20260901-tp41.md)**. Branch `tp41-cf-access-repair`.
|
||||||
|
|
||||||
|
**✅ Live erledigt:** Session 720h (Wildcard/vw/Warp-Login) · login_method-Loch zu (reusable Policy
|
||||||
|
`bf-users` = nur `email:christoph`+`email:geli`) · `8e44f5d1` von 4 Apps auf **nur Terpenfinder**
|
||||||
|
entkoppelt · Access-**Gruppen** `zt-humans`/`zt-home`/`zt-automation`, Policies zeigen darauf · **vw voll
|
||||||
|
Bypass** (Lebensader offen) + **`vw/admin` per WAF-Regel öffentlich geblockt** (CF-Edge + Mobilfunk
|
||||||
|
verifiziert) · API-Token auf das vollständige `…4148` konsolidiert (User hat Service-Tokens- + Zero-Trust-
|
||||||
|
Write ergänzt), altes `…2174` revoken.
|
||||||
|
|
||||||
|
**✅ T4 DURCH (2026-09-01, Variante B):** ein Access-Service-Token **`zt-svc`** (`65f1821a`, rotiert) über
|
||||||
|
Gruppe `zt-automation`; Terpene-Worker per `wrangler secret put TERPENE_SVC_CLIENT_ID` auf zt-svc
|
||||||
|
umgestellt (Worker-Pin `index.js:44`); `terpene-verify`+`-v2` **gelöscht**; `test_whoami` grün.
|
||||||
|
**Token-Altlast geklärt:** `b00722` = LIVE Caddy-DNS-Token (LXC130 caddy.env, SSoT), `d47435` = stale
|
||||||
|
Kopie (kein Consumer) → **User revoket `d47435`**; `credentials.env`-Kopie auf b00722 realignt.
|
||||||
|
|
||||||
|
**✅ T3 APPLIED (2026-09-01):** Default-Profil (`0197d1b3`; das custom „Cloudflare Mesh" matcht nur den
|
||||||
|
`warp_connector`) `fallback_domains += emsenhuber.net → [10.40.20.36, 10.40.20.37]`; Tunnel-Route
|
||||||
|
`10.40.0.0/16` (`d1da7c8a`) da, kein `10/8`-Exclude → routbar. ⏳ Client-Verify: WARP neu verbinden.
|
||||||
|
**✅ T2 DURCH (2026-09-01):** WAF-Regel `f26744eb` gelöscht; Caddy `vw`-Block `respond @cf_admin 404` bei
|
||||||
|
Header `CF-Connecting-IP` (LXC130). Verify: Edge `/admin`=404, lokal/WARP `/admin`=200. ✅
|
||||||
|
|
||||||
|
**✅ T5 DURCH (2026-09-01, User-bestätigt):** GitHub-Login läuft; Google-Login läuft nach dem
|
||||||
|
Consent-Screen-Fix (§A des Follow-up-Handoffs: User Type *Internal*→*External*/veröffentlicht).
|
||||||
|
Callback für beide `https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/callback`.
|
||||||
|
**❌ Facebook/Meta für TP41 abgesagt (2026-09-01, User-Entscheid):** optional, kein Bedarf — Geli ist
|
||||||
|
über OTP `angelikahy@outlook.de` + Google `pillepalle670@gmail.com` voll gedeckt. **Keine Facebook-App,
|
||||||
|
kein CF-Facebook-IdP angelegt → nichts aufzuräumen.** Instagram hat ohnehin keinen CF-IdP.
|
||||||
|
**✅ App-Launcher (D1):** je eine Bookmark-Kachel vw/hass/terpenfinder, Wildcard-Kachel raus, Launcher
|
||||||
|
gestylt (Follow-up-Handoff §D, Policies unverändert).
|
||||||
|
|
||||||
|
**✅ Geli in WARP — CF-seitig nichts zu tun (2026-09-01 live geprüft):** Device-Enrollment läuft über
|
||||||
|
die App **„Warp Login App"** (`6c32fe23…`, type `warp`) → Policy `bf-users` → Gruppe **`zt-humans`**, die
|
||||||
|
Gelis beide Mails **schon enthält** → sie **darf bereits enrollen** (deshalb war „Device enrollment
|
||||||
|
permissions" nicht als fehlendes Setting zu finden). Terpenfinder-`geli`-Policy lässt sie per E-Mail durch.
|
||||||
|
✅ **Client am 2026-09-10 via Android erfolgreich eingerichtet** (WARP installiert, Team `emsenhuber`,
|
||||||
|
Login OTP `angelikahy@outlook.de` — User-Bestätigung); damit ist der einzige verbliebene Handgriff erledigt.
|
||||||
|
🔑 **Permission-Frage beantwortet:** CF-seitig **keine** nötig. Für generelle WARP/Enrollment/User-Verwaltung
|
||||||
|
per Account-Token: *Access: Apps and Policies → Edit* + *Access: Organizations, IdPs, and Groups → Edit*
|
||||||
|
(User erteilt 2026-09-01). **Seat-Verwaltung (`/access/seats`) bleibt per MCP gesperrt** — `10405 Method
|
||||||
|
not allowed for this authentication scheme` (Endpoint will User-Token, nicht Account-Token; kein Scope-Fix,
|
||||||
|
für Enroll irrelevant).
|
||||||
|
|
||||||
|
**🔻 Optionale Restpunkte (bewusst nicht gezogen, kein Blocker):** **T6** optional Rate-Limit ·
|
||||||
|
🔴 `TERPENE_ANDROID_KEYSTORE_PW`-Rotation (→ **TP30 Posten #7**, Transkript-Leak) · **Security-Cleanup:**
|
||||||
|
die `geli`-Terpenfinder-Policy hat **OR-Semantik** und enthält den breiten CGNAT-Bereich
|
||||||
|
`94.154.138.0/24` als IP-Include → jeder aus dieser geteilten Range käme **ohne Login** rein. Über WARP ist
|
||||||
|
der Egress ohnehin Cloudflare (nicht diese IP) → der IP-Include wäre dann überflüssig und entfernbar.
|
||||||
|
**d47435-Revoke ✅ · T3-WARP-Client-Verify ✅** (User, 2026-09-01).
|
||||||
|
|
||||||
|
**Abgrenzung:** hass/gitea bleiben (diese Runde) öffentlich; Terpenfinder-Human-Policies nicht angefasst;
|
||||||
|
**Gitea-Least-Privilege** = eigener künftiger TP (jedes Projekt repo-scoped Login, Admin nur Claude/Christoph).
|
||||||
|
- Bezug **TP11** (Monitoring): eine Plattenfüllstand-Warnung hätte das Wochen früher gezeigt.
|
||||||
|
|
||||||
|
### TP43 — dw-Standort-DNS-Uplink (site-spezifische Overrides) ▸ ♻️ **REOPENED 2026-09-11 → Volltext zurück in [`teilprojekte.md`](teilprojekte.md) (Abschnitt TP43)**
|
||||||
|
|
||||||
|
Kurz zwischen 2026-09-11 verworfen und am selben Tag reopened: die 420-Prämisse war gegenstandslos
|
||||||
|
(public CF-Worker, via WARP/TP41 gelöst), aber die **interne** BF-Namensauflösung über WG am dw blieb
|
||||||
|
dabei offen (`portainer.bf.emsenhuber.net` am dw nicht erreichbar). Korrigierter Scope + verworfene
|
||||||
|
420-Historie stehen jetzt aktiv in `teilprojekte.md`. Gitea-Issue #32 = `status:todo` · `thema:firewall-dns`.
|
||||||
|
|
||||||
|
### TP42 — Namens-Vereinheitlichung: ein interner Suffix bf.emsenhuber.net ▸ ✅ **DURCH (2026-09-02)** — ein interner Suffix `bf.emsenhuber.net`, `.bf.lan` raus, `.16` (adguard_bf1) abgebaut, Compliance 140/140. Offen nur der an **TP37** gekoppelte Nautobot-Doku-Teil.
|
||||||
|
|
||||||
|
**Auslöser (2026-09-01, TP41-Randbefund):** live gemessen liefern die realen Client-Resolver
|
||||||
|
`10.40.20.36`/`.37` `*.emsenhuber.net → 10.40.20.40` (Caddy) — korrekt. Daneben existiert aber ein
|
||||||
|
zweiter, gedrifteter interner Namensraum: der **Core** trägt in `/ip dns static` 11 Einträge auf
|
||||||
|
`*.bf.lan`, der **Caddyfile** doppelt jeden internen Namen als `*.bf.emsenhuber.net`, und die alten
|
||||||
|
AdGuard `.16`/`.17` antworten noch mit `nginx*.bf.emsenhuber.net` bzw. totem VLAN1 `10.40.41.123`.
|
||||||
|
Christoph: „aus emsenhuber.net wurde schon wieder bf.lan — das müssen wir ein für alle mal aus allen
|
||||||
|
Geräten rausprügeln und in Nautobot festhalten."
|
||||||
|
|
||||||
|
**Ziel:** genau **ein** interner Namensraum `emsenhuber.net` (Split-Horizon `.36/.37 → .40`). `.bf.lan`
|
||||||
|
und `.bf.emsenhuber.net` werden entfernt; Sollzustand in Nautobot dokumentiert.
|
||||||
|
|
||||||
|
**Scope (Reihenfolge = eigene Session):**
|
||||||
|
1. **Bestandsaufnahme** aller Fundstellen: Core `/ip dns static` (`*.bf.lan`), DHCP-Option
|
||||||
|
`domain`/Search-Domain je Netz, Caddyfile-Aliase (`*.bf.emsenhuber.net`), AdGuard-Rewrites `.36/.37`,
|
||||||
|
stale `.16/.17`, Geräte-`identity`.
|
||||||
|
2. **Entscheidung Zielschema** (nur `emsenhuber.net`, ggf. `bf.emsenhuber.net` als *einzige* interne
|
||||||
|
Zone — Christoph-Entscheid) und Migrationsreihenfolge ohne DNS-Loch.
|
||||||
|
3. **Umsetzen:** Core-DNS + DHCP + Caddyfile angleichen; stale `.16/.17` klären/abbauen.
|
||||||
|
4. **Nautobot:** interne Namen/Records als Sollzustand erfassen (Bezug TP37 Datenmodell).
|
||||||
|
|
||||||
|
**Abgrenzung:** kein CF-Public-DNS-Umbau (Zone `emsenhuber.net` öffentlich bleibt); kein Resolver-Swap
|
||||||
|
(`.36/.37` bleiben); `.1`-als-DNS-Reste auf Clients sind separat/tolerierbar (User: „bis jetzt kein
|
||||||
|
Problem"). **Abh.:** TP37 (Nautobot-Datenmodell) für Punkt 4; sonst nichts Offenes.
|
||||||
|
|
||||||
|
#### ✅ Bestandsaufnahme LIVE (2026-09-02) — Drift hat 80 % schon erledigt
|
||||||
|
Gegen die Geräte gemessen (MikroMCP + dig), nicht aus dem Handoff:
|
||||||
|
|
||||||
|
| Fundstelle | Handoff 09-01 | Live 09-02 |
|
||||||
|
|---|---|---|
|
||||||
|
| Core `/ip dns static` | 11× `*.bf.lan` | ✅ **0× `.bf.lan`** — 10× `*.bf.emsenhuber.net` (deckt sich mit GC-Template Z.284–293) |
|
||||||
|
| DHCP `domain` (7 Netze) | „prüfen" | `emsenhuber.net` (GC-Template Z.133 = gleich ⇒ **keine Drift**), DNS `.36/.37` |
|
||||||
|
| `.36/.37` (Soll) | aktiv | `vw.emsenhuber.net→.40` ✅ · `macmini.bf.emsenhuber.net→.20.167` ✅ · **`macmini.emsenhuber.net→leer`** ❌ |
|
||||||
|
| Caddyfile | `*.bf.emsenhuber.net`-Aliase | unverändert, **konsistent** mit Core-Static |
|
||||||
|
| `.16`/`.17` | tot | laufen (:53), liefern totes VLAN1; **kein Core-Objekt** (Firewall/NAT/DNS-Upstream/Addr-List) hängt an `.16` |
|
||||||
|
|
||||||
|
**Kernkonflikt:** DHCP verteilt `emsenhuber.net` (bare), Records leben unter `bf.emsenhuber.net` ⇒ bare `macmini` löst nicht.
|
||||||
|
|
||||||
|
#### ✅ Entscheidungen (User, 2026-09-02)
|
||||||
|
1. **Zielschema = `bf.emsenhuber.net`** (der wenig-Arbeit-Weg): DHCP-domain → `bf.emsenhuber.net`, dann
|
||||||
|
löst `macmini` bare korrekt; Core-Static + Caddyfile bleiben (tragen schon `.bf.`). Öffentliche Zone
|
||||||
|
`emsenhuber.net` bleibt getrennt.
|
||||||
|
2. **`.16` (adguard_bf1) abbauen**, **`.17` (adguard_fm2 = Standort FM) NICHT anfassen.**
|
||||||
|
|
||||||
|
#### ✅ Umsetzung DURCH (2026-09-02)
|
||||||
|
- **(A) DHCP-domain `emsenhuber.net → bf.emsenhuber.net` über GC ✅** — kein Direkt-Write:
|
||||||
|
`mikrotik_routeros.j2` Z.133 gehoben → `push-gc-template.py` (Commit `7a4068c4`, **Nebenbefund:** das
|
||||||
|
Remote-Template hing dem lokalen hinterher — es trug noch `*.bf.lan`+`router.lan`, obwohl Gerät+lokal
|
||||||
|
längst `.bf.emsenhuber.net` fuhren; der Push hat das GC-Repo an den Live-Stand angeglichen) →
|
||||||
|
GitRepo-Sync (SUCCESS) → manueller ConfigPlan **`tp42-dhcp-domain`** (`set [find] domain=…`, weil
|
||||||
|
`add` das Netz doppelt anlegen würde) → Deploy an **crs326-core** → Live **7/7 = bf.emsenhuber.net**,
|
||||||
|
Compliance **140/140, keine Drifts**. Der Job steht dauerhaft in `nautobot/gc-configplan.py`.
|
||||||
|
- **(B) `.16`-Teardown ✅** — `adguard_bf1` lag im geteilten Portainer-Stack **195 `adguard`** (Docker-LXC
|
||||||
|
`.126`, **ep2** — *nicht* ep14) **zusammen mit `adguard_fm2` (`.17`, Standort FM!)**. Deshalb **nicht**
|
||||||
|
Stack löschen, sondern compose auf nur `.17` getrimmt + Stack redeployt (Prune): `.16`-Container weg,
|
||||||
|
`.16:53` tot; **`.17` läuft** (⚠️ ~40 s mit-neugestartet durch den Redeploy — kurzer FM-DNS-Blip,
|
||||||
|
danach sauber). Rollback-compose gesichert. 🧹 Daten-Dirs `/opt/docker/adguard_bf1/*` bleiben als
|
||||||
|
harmlose Leichen auf dem LXC.
|
||||||
|
- **(C) Nautobot-Soll** (Scope-Punkt 4) bleibt an **TP37** gekoppelt (Datenmodell), separat — kein Blocker.
|
||||||
|
|
||||||
|
**Status:** ✅ **Namensraum vereinheitlicht (2026-09-02)** — ein interner Suffix `bf.emsenhuber.net`,
|
||||||
|
`.bf.lan` raus, `.16` abgebaut, Compliance 140/140. Offen nur der TP37-gekoppelte Nautobot-Doku-Teil.
|
||||||
|
Handoff: [`docs/handoff-20260901-tp42-naming.md`](handoff-20260901-tp42-naming.md).
|
||||||
|
|||||||
1032
docs/teilprojekte.md
1032
docs/teilprojekte.md
File diff suppressed because it is too large
Load Diff
76
docs/tp23-stufe6-bypass-sperre-runbook.md
Normal file
76
docs/tp23-stufe6-bypass-sperre-runbook.md
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
# TP23 Stufe 6 — DNS-Bypass-Sperre (Runbook für eine SEPARATE, VOR-ORT-Session)
|
||||||
|
|
||||||
|
> 🔴🔴 **HARTES GATE: NUR VOR ORT ausführen. NICHT aus der Ferne, nicht während Christoph
|
||||||
|
> abwesend ist.** (Notiert 2026-09-06: Christoph ist ~1 Woche nicht vor Ort → in diesem Fenster
|
||||||
|
> **gar nicht anfangen**, auch nicht die log-only-Vorstufe scharf lassen.)
|
||||||
|
>
|
||||||
|
> Grund: Die Sperre macht das AGH-Paar `.36`/`.37` zum **harten DNS-SPOF**. Bis heute kann jeder
|
||||||
|
> Client bei einem AGH-Ausfall auf einen externen Resolver ausweichen; **nach** der Sperre nicht
|
||||||
|
> mehr — ein AGH-Ausfall = **hausweiter DNS-Ausfall** (betrifft die Familie), und ohne physischen
|
||||||
|
> Zugriff gibt es keinen schnellen Rollback. Eine falsch platzierte Drop-Regel kann außerdem den
|
||||||
|
> **Fernzugang selbst** (WG/cf-mesh) aussperren, mit dem man den Fehler zurücknehmen wollte.
|
||||||
|
|
||||||
|
## Ziel
|
||||||
|
Clients dürfen plain **DNS (udp/tcp 53)** und **DoT (853)** nur noch zu den erlaubten Resolvern
|
||||||
|
(`10.40.20.36`, `10.40.20.37`, Core `10.40.X.1`) schicken. Externe DNS-Server (Smart-TV mit
|
||||||
|
`8.8.8.8` hartkodiert, App-eigene Resolver) werden geblockt → **kein Umgehen des Filters**.
|
||||||
|
Das ist der letzte Baustein von TP23 und lebt in der **TP5-Firewall** (`forward`-Kette).
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
**Erfüllt (Stand 2026-09-06):**
|
||||||
|
- Self-Service-Oberfläche steht und ist getestet (HA-Dashboard `dns-ausnahmen`, Stufe 5) → ein
|
||||||
|
geblockter Dienst kann **ohne Admin** temporär freigegeben werden. Das war die harte
|
||||||
|
Reihenfolge-Regel („erst der Ersatz, dann die Sperre").
|
||||||
|
- `dns-bypass-exempt`-Gruppe existiert auf dem Resolver-Paar (AGH-Client-Seite).
|
||||||
|
|
||||||
|
**Noch zu bauen (Teil dieser Session, VOR dem Drop):**
|
||||||
|
- **Firewall-Exempt** ist noch NICHT da: eine Address-List `dns-bypass-exempt` (permanent) mit dem
|
||||||
|
dev-MacBook (`10.40.10.183` **und** `10.40.10.82`, dual-homed) + Agent-Toolchain, plus eine
|
||||||
|
`accept`-Regel **vor** dem Drop. Ohne die verliert der Entwicklungsrechner seinen Ausweg.
|
||||||
|
- Ad-hoc-JIT: Address-List `jit-dns-bypass` mit `timeout` nach dem Muster `jit-admin`
|
||||||
|
(`routeros/tp5-etappe4-input-mgmt.rsc`) — selbstverfallende Freigabe.
|
||||||
|
|
||||||
|
## Bekannte Grenze (offen benennen, nicht „lösen")
|
||||||
|
Die Sperre greift auf **Port** (53/853). **DoH läuft über 443** und ist damit portgleich zu HTTPS —
|
||||||
|
ein Gerät, das DoH gegen `8.8.8.8:443` fährt, umgeht die Sperre. Das per SNI/IP zu schließen ist
|
||||||
|
ein eigenes, größeres Thema (nicht Teil von TP23). Für den Alltag (TVs/IoT mit Plain-DNS) reicht die
|
||||||
|
Port-Sperre.
|
||||||
|
|
||||||
|
## Sichere Methodik — log-only zuerst, Drop zuletzt
|
||||||
|
1. **Exempt + JIT bauen** (via Nautobot GC, `ip-firewall-filter` steht unter Drift-Kontrolle → **kein
|
||||||
|
Direkt-Write**): `accept` für `dns-bypass-exempt`/`jit-dns-bypass` → 53/853, platziert **vor** der
|
||||||
|
künftigen Drop-Regel. Erst hier abnehmen, dass die Exempts wirklich durchkommen.
|
||||||
|
2. **Log-only-Vorstufe**: eine Regel `TP5-WOULD-DROP-DNS`, die LAN→extern auf `dst-port 53,853`
|
||||||
|
(udp+tcp), **nicht** zu `.36/.37/Core`, **nur loggt** (in den Kollektor `10.40.20.18:514`, wie die
|
||||||
|
bestehende `TP5-WOULD-DROP`). **Mehrere Tage laufen lassen** und den Drop-Log lesen: *welche*
|
||||||
|
Geräte reden mit *welchen* externen Resolvern? Jedes davon ist ein Kandidat, der nach dem Drop
|
||||||
|
bricht — vorab entscheiden (Reservierung auf AGH, Exempt, oder bewusst brechen lassen).
|
||||||
|
🔑 Merkregel aus dem E5-Drop-Log: Geräte fallen oft mit **udp/161/andere** nebenbei auf — nur die
|
||||||
|
53/853-Zeilen zählen hier.
|
||||||
|
3. **Erst nach ruhigem/verstandenem Log**: die Log-only-Regel auf `action=drop` (oder `reject` mit
|
||||||
|
`icmp-port-unreachable`, damit Clients schneller auf den nächsten Resolver fallen) heben —
|
||||||
|
**mit Auto-Revert-Absicherung** (Scheduler/Netwatch, der die Regel nach X Minuten deaktiviert,
|
||||||
|
falls nicht bestätigt), analog zum TP5-E3/E4-Vorgehen. **Vor Ort**, mit offener Konsole.
|
||||||
|
|
||||||
|
## Was NICHT gesperrt werden darf (sonst Selbst-Aussperrung)
|
||||||
|
- `.36`/`.37` → CF-DoH-Upstream (443) und Core-Resolver-Egress: die Resolver müssen raus.
|
||||||
|
- Core `10.40.X.1` DoH-Egress.
|
||||||
|
- **cf-mesh / WG-Pfad** (Fernzugang) — die Drop-Regel matcht nur `dst-port 53,853`; trotzdem vor dem
|
||||||
|
Scharfschalten gegenprüfen, dass Management-Input (`cf-mesh`, HA `8728`, Nautobot `22`) unberührt
|
||||||
|
bleibt.
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
- GC-Deploy der vorherigen Version (Regel zurück auf `disabled`/entfernt) — bzw. der Auto-Revert.
|
||||||
|
- Fällt das AGH-Paar bei aktiver Sperre aus: Drop-Regel deaktivieren (Vor-Ort-Konsole) → Clients
|
||||||
|
können sofort wieder externe Resolver nutzen.
|
||||||
|
|
||||||
|
## Abnahme
|
||||||
|
- Exempt-Host (dev-MacBook) löst extern weiter auf; ein Test-Gerät mit hartkodiertem `8.8.8.8` löst
|
||||||
|
**nicht** mehr (aber über AGH schon).
|
||||||
|
- Self-Service: eine geblockte Domain per HA-Button freigeben → wirkt; nach Ablauf wieder gefiltert.
|
||||||
|
- Compliance nach dem GC-Deploy grün (`python3 nautobot/gc-transaktion.py status`, GC 140/140).
|
||||||
|
|
||||||
|
## Verweise
|
||||||
|
- Stufe 4/5 (fertig): `adguard/README.md`, `adguard/ha-self-service.md`, `adguard/agh-exception.py`.
|
||||||
|
- JIT-/Firewall-Muster: `routeros/tp5-etappe4-input-mgmt.rsc`; GC-Pipeline: `nautobot/gc-configplan.py`.
|
||||||
|
- Kontext/Reihenfolge-Regel: TP23-Abschnitt in `docs/teilprojekte.md`.
|
||||||
@@ -22,6 +22,7 @@ Damit wird aus "irgendwo zwischen 15:07 und 15:09 war es schlecht" ein Satz mit
|
|||||||
"""
|
"""
|
||||||
import argparse
|
import argparse
|
||||||
import csv
|
import csv
|
||||||
|
import glob
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
@@ -98,24 +99,45 @@ def lade_pings_alt(pfad):
|
|||||||
return treffer
|
return treffer
|
||||||
|
|
||||||
|
|
||||||
def geraete(run_dir):
|
def _sampler_funk(pfad):
|
||||||
"""Liste von Geraeten: (rolle, ping-Liste, funk-Liste).
|
"""funk-Liste (sekunde, iface, signal, phy_rate_oder_None) aus einer sampler-CSV."""
|
||||||
|
funk = []
|
||||||
|
if os.path.exists(pfad):
|
||||||
|
for r in csv.DictReader(open(pfad)):
|
||||||
|
if r.get("sta_signal"):
|
||||||
|
funk.append((sec(r["ts_local"]), r.get("sta_iface") or "",
|
||||||
|
int(r["sta_signal"]),
|
||||||
|
int(r["sta_tx_rate"]) if (r.get("sta_tx_rate") or "").isdigit() else None))
|
||||||
|
return funk
|
||||||
|
|
||||||
funk-Liste = (sekunde, iface, signal, phy_rate_oder_None). Die PHY-Rate gibt es nur
|
|
||||||
fuer die Sonde (sampler.csv, 1-s-Takt); fuer die Referenz liefert beide-telefone.csv
|
def geraete(run_dir):
|
||||||
nur AP und Signal im 2-s-Takt. Das ist bewusst so und wird unten beim Ruhezustand
|
"""Liste benannter Geraete: (name, ping-Liste, funk-Liste).
|
||||||
beruecksichtigt -- ohne Rate ist der Befund schwaecher und wird auch so benannt.
|
|
||||||
|
funk-Liste = (sekunde, iface, signal, phy_rate_oder_None).
|
||||||
|
|
||||||
|
Seit 2026-09-04 (N-Geraete-Umbau) schreibt die Harness pro Geraet walk-ping-<name>.csv
|
||||||
|
UND sampler-<name>.csv -- jedes Paar wird als EIN benanntes Geraet gelesen, beliebig viele,
|
||||||
|
ohne feste Sonde/Referenz-Rollen. Faellt auf das alte 2-Rollen-Schema zurueck (sampler.csv /
|
||||||
|
walk-ping.csv / walk-ping-iphone.csv / beide-telefone.csv), damit Laeufe vor dem Umbau
|
||||||
|
weiter auswertbar bleiben. Unterschieden wird an der NACKTEN sampler.csv/walk-ping.csv:
|
||||||
|
die schreibt nur das alte Schema -- die neue Harness nie (immer mit -<name>). Ohne diese
|
||||||
|
Klammer wuerde der glob unten die walk-ping-iphone.csv eines Alt-Laufs fassen und dessen
|
||||||
|
Sonde (walk-ping.csv, ohne Suffix) stillschweigend verlieren.
|
||||||
"""
|
"""
|
||||||
pfad = lambda n: os.path.join(run_dir, n)
|
pfad = lambda n: os.path.join(run_dir, n)
|
||||||
|
|
||||||
sonde_funk = []
|
legacy = os.path.exists(pfad("sampler.csv")) or os.path.exists(pfad("walk-ping.csv"))
|
||||||
if os.path.exists(pfad("sampler.csv")):
|
if not legacy:
|
||||||
for r in csv.DictReader(open(pfad("sampler.csv"))):
|
gs = []
|
||||||
if r.get("sta_signal"):
|
for pp in sorted(glob.glob(os.path.join(run_dir, "walk-ping-*.csv"))):
|
||||||
sonde_funk.append((sec(r["ts_local"]), r.get("sta_iface") or "",
|
name = os.path.basename(pp)[len("walk-ping-"):-len(".csv")]
|
||||||
int(r["sta_signal"]),
|
gs.append((name, lade_pings(pp), _sampler_funk(pfad("sampler-%s.csv" % name))))
|
||||||
int(r["sta_tx_rate"]) if (r.get("sta_tx_rate") or "").isdigit() else None))
|
if gs:
|
||||||
|
return gs
|
||||||
|
|
||||||
|
# --- Legacy: 2-Rollen-Schema (Laeufe vor 2026-09-04) ---
|
||||||
|
sonde_funk = _sampler_funk(pfad("sampler.csv"))
|
||||||
ref_funk = []
|
ref_funk = []
|
||||||
if os.path.exists(pfad("beide-telefone.csv")):
|
if os.path.exists(pfad("beide-telefone.csv")):
|
||||||
for r in csv.DictReader(open(pfad("beide-telefone.csv"))):
|
for r in csv.DictReader(open(pfad("beide-telefone.csv"))):
|
||||||
@@ -189,6 +211,9 @@ def main():
|
|||||||
gs = geraete(a.dir)
|
gs = geraete(a.dir)
|
||||||
if not any(g[1] or g[2] for g in gs):
|
if not any(g[1] or g[2] for g in gs):
|
||||||
raise SystemExit("Weder Ping-Antworten noch Funkproben gefunden.")
|
raise SystemExit("Weder Ping-Antworten noch Funkproben gefunden.")
|
||||||
|
# Alte 2-Rollen-Laeufe tragen die Namen "Referenz"/"Sonde" und bekommen den Rollen-Text;
|
||||||
|
# neue N-Geraete-Laeufe tragen Geraetenamen (iphone/s22/ipad/...) -- ohne Rollen-Erklaerung.
|
||||||
|
legacy_rollen = any("Sonde" in r or "Referenz" in r for r, _, _ in gs)
|
||||||
|
|
||||||
rooms = []
|
rooms = []
|
||||||
for m in csv.DictReader(open(os.path.join(a.dir, "markers.csv"))):
|
for m in csv.DictReader(open(os.path.join(a.dir, "markers.csv"))):
|
||||||
@@ -209,9 +234,12 @@ def main():
|
|||||||
for i, r in enumerate(rooms)]
|
for i, r in enumerate(rooms)]
|
||||||
|
|
||||||
mehrere = len(gs) > 1
|
mehrere = len(gs) > 1
|
||||||
if mehrere:
|
if mehrere and legacy_rollen:
|
||||||
print("REFERENZ = iPhone, roamt frei (was du hier wirklich hast)")
|
print("REFERENZ = iPhone, roamt frei (was du hier wirklich hast)")
|
||||||
print("SONDE = S22, auf den Gegen-AP gezwungen (was die Rückfallebene noch kann)\n")
|
print("SONDE = S22, auf den Gegen-AP gezwungen (was die Rückfallebene noch kann)\n")
|
||||||
|
elif mehrere:
|
||||||
|
print("Ein Block je Raum, eine Zeile je Gerät (%s) — alle frei roamend.\n"
|
||||||
|
% ", ".join(r for r, _, _ in gs))
|
||||||
print("%-15s %-17s %5s %-22s %-17s %s"
|
print("%-15s %-17s %5s %-22s %-17s %s"
|
||||||
% ("Raum", "Rolle", "Dauer", "Signal min/⌀/max", "Verlust", "längste Lücke"))
|
% ("Raum", "Rolle", "Dauer", "Signal min/⌀/max", "Verlust", "längste Lücke"))
|
||||||
print("-" * 100)
|
print("-" * 100)
|
||||||
@@ -262,9 +290,13 @@ def main():
|
|||||||
print("\nLesehilfe: eine längste Lücke über ~2 s ist für einen Videocall eine hörbare")
|
print("\nLesehilfe: eine längste Lücke über ~2 s ist für einen Videocall eine hörbare")
|
||||||
print("Unterbrechung, über 10 s ist der Call praktisch tot. Signal unter −70 dBm ist die")
|
print("Unterbrechung, über 10 s ist der Call praktisch tot. Signal unter −70 dBm ist die")
|
||||||
print("Zone, in der das passiert.")
|
print("Zone, in der das passiert.")
|
||||||
if mehrere:
|
if mehrere and legacy_rollen:
|
||||||
print("\nDie interessante Spalte ist der Unterschied: gute Referenz + schlechte Sonde")
|
print("\nDie interessante Spalte ist der Unterschied: gute Referenz + schlechte Sonde")
|
||||||
print("= hier hängt alles an EINEM AP. Beide schlecht = echtes Loch ohne Rückfallebene.")
|
print("= hier hängt alles an EINEM AP. Beide schlecht = echtes Loch ohne Rückfallebene.")
|
||||||
|
elif mehrere:
|
||||||
|
print("\nDie interessante Spalte ist der Unterschied ZWISCHEN den Geräten am selben Ort:")
|
||||||
|
print("streuen Signal/Verlust stark, liegt es am Gerät (Antenne/Standby), nicht am Funk;")
|
||||||
|
print("sind alle an einem Ort schlecht, ist es ein echtes Loch der Ausleuchtung.")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
105
docs/tp25-pin-multi.sh
Executable file
105
docs/tp25-pin-multi.sh
Executable file
@@ -0,0 +1,105 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# TP25 — MEHRERE Clients gleichzeitig band-genau auf je EINEN AP pinnen.
|
||||||
|
#
|
||||||
|
# Wozu (2026-09-04): die Gehweg-Messung soll pro AP getrennt zeigen, wie weit er reicht. Dazu
|
||||||
|
# wird je Sonde GENAU EIN cap-wifiN erlaubt (also ein AP UND ein Band) und alle anderen per
|
||||||
|
# access-list abgewiesen. Das iPhone bleibt bewusst UNGEPINNT (frei roamende Referenz).
|
||||||
|
#
|
||||||
|
# Unterschied zu tp25-pin-s22.sh: das pinnt EIN Geraet AP-genau (beide Baender). Hier: N
|
||||||
|
# Geraete, band-genau, je eigener Tag TP25-PINM-<name> -- damit `off` sauber nur diese entfernt.
|
||||||
|
#
|
||||||
|
# set: TP25_PINSPEC="ipad|CA:..|ap1|5;macbook|56:..|ap2|5;s22|2A:..|ap3|5" bash tp25-pin-multi.sh set
|
||||||
|
# verify: bash tp25-pin-multi.sh verify # zeigt je Sonde das aktuelle Interface
|
||||||
|
# off: bash tp25-pin-multi.sh off # alle TP25-PINM-* weg
|
||||||
|
#
|
||||||
|
# Band im Spec: "5" = 5 GHz, "24"/"2"/"2.4" = 2,4 GHz. AP: ap1|ap2|ap3 (wie in der Live-Karte).
|
||||||
|
#
|
||||||
|
# 🔴 Die cap-wifiN-Zuordnung wird bei JEDEM Aufruf frisch aus der Karte (tp25-pin-s22.sh map)
|
||||||
|
# gelesen -- sie wandert bei jedem CAP-Rejoin. Niemals cachen.
|
||||||
|
# 🔴 access-list steht unter KEINER ComplianceRule -> kein Nautobot-Drift. Jede Regel traegt
|
||||||
|
# ihren Tag; `off` raeumt vollstaendig auf.
|
||||||
|
# 🔴 Bewusst KEINE assoziativen Arrays (declare -A): macOS /bin/bash ist 3.2. Lookup per grep.
|
||||||
|
|
||||||
|
set -uo pipefail
|
||||||
|
|
||||||
|
TAG_PREFIX="TP25-PINM"
|
||||||
|
ROUTER_IP="${TP25_MANAGER_IP:-10.40.99.1}"
|
||||||
|
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
ZIEL="${1:-verify}"
|
||||||
|
|
||||||
|
PW="$(grep -m1 '^CRS326_CORE_PASS=' ~/.secrets/credentials.env | cut -d= -f2- | sed 's/^["'"'"']//; s/["'"'"']$//')"
|
||||||
|
[[ -n "$PW" ]] || { echo "CRS326_CORE_PASS nicht in ~/.secrets/credentials.env gefunden" >&2; exit 1; }
|
||||||
|
|
||||||
|
# 🔴 sshpass -e statt -p: PW nie in die Kommandozeile (ps/Trace-Leak, 2026-08-22). Ausgabe zusaetzlich maskiert.
|
||||||
|
ros() {
|
||||||
|
SSHPASS="$PW" sshpass -e ssh -o StrictHostKeyChecking=no -o ConnectTimeout=8 \
|
||||||
|
"admin@${ROUTER_IP}" "$1" 2>&1 | sed "s/${PW//\//\\/}/***/g"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Live-Karte (cap-wifiN=apX/Band, je Zeile) als String. Reuse von tp25-pin-s22.sh (map).
|
||||||
|
MAP=""
|
||||||
|
declare -a ALL_IFACES
|
||||||
|
lade_karte() {
|
||||||
|
MAP="$(bash "$HERE/tp25-pin-s22.sh" map)"
|
||||||
|
[[ -n "${MAP//[[:space:]]/}" ]] || { echo "Live-Karte leer -- haengen die CAPs am Manager?" >&2; exit 1; }
|
||||||
|
ALL_IFACES=()
|
||||||
|
while IFS='=' read -r iface _label; do [[ -n "$iface" ]] && ALL_IFACES+=("$iface"); done <<< "$MAP"
|
||||||
|
}
|
||||||
|
|
||||||
|
label_of() { grep -m1 "^$1=" <<< "$MAP" | cut -d= -f2-; }
|
||||||
|
|
||||||
|
# ap + band -> cap-wifiN (aus der Karte). Band-Eingabe normalisieren.
|
||||||
|
ziel_iface() { # $1=ap $2=band
|
||||||
|
local want
|
||||||
|
case "$2" in
|
||||||
|
5|5g|5G|5GHz) want="$1/5GHz" ;;
|
||||||
|
24|2|2.4|2,4|2g|2G) want="$1/2,4GHz" ;;
|
||||||
|
*) echo "" ; return ;;
|
||||||
|
esac
|
||||||
|
grep -m1 "=${want}\$" <<< "$MAP" | cut -d= -f1
|
||||||
|
}
|
||||||
|
|
||||||
|
kick() { ros "/interface wifi registration-table remove [find where mac-address=\"$1\"]" >/dev/null; }
|
||||||
|
aufraeumen_alle() { ros "/interface wifi access-list remove [find where comment~\"$TAG_PREFIX\"]" >/dev/null; }
|
||||||
|
|
||||||
|
case "$ZIEL" in
|
||||||
|
set)
|
||||||
|
[[ -n "${TP25_PINSPEC:-}" ]] || { echo "TP25_PINSPEC fehlt (name|mac|ap|band;...)" >&2; exit 2; }
|
||||||
|
lade_karte
|
||||||
|
aufraeumen_alle
|
||||||
|
IFS=';' read -ra ENTRIES <<< "$TP25_PINSPEC"
|
||||||
|
for e in "${ENTRIES[@]}"; do
|
||||||
|
[[ -z "${e//[[:space:]]/}" ]] && continue
|
||||||
|
IFS='|' read -r name mac ap band <<< "$e"
|
||||||
|
[[ -n "$name" && -n "$mac" && -n "$ap" && -n "$band" ]] || { echo "Spec-Eintrag unvollstaendig: '$e'" >&2; exit 2; }
|
||||||
|
ziel="$(ziel_iface "$ap" "$band")"
|
||||||
|
[[ -n "$ziel" ]] || { echo "Kein Interface fuer $ap/$band in der Karte -- AP am Manager? Abbruch." >&2; exit 1; }
|
||||||
|
tag="${TAG_PREFIX}-${name}"
|
||||||
|
# Alle Interfaces AUSSER dem Ziel abweisen -> nur $ziel bleibt erlaubt (AP + Band erzwungen).
|
||||||
|
gesperrt=""
|
||||||
|
for i in "${ALL_IFACES[@]}"; do
|
||||||
|
[[ "$i" == "$ziel" ]] && continue
|
||||||
|
ros "/interface wifi access-list add mac-address=$mac interface=$i action=reject comment=\"$tag\"" >/dev/null
|
||||||
|
gesperrt="$gesperrt $i"
|
||||||
|
done
|
||||||
|
echo "$name ($mac) -> $ziel ($(label_of "$ziel")); abgewiesen:$gesperrt"
|
||||||
|
kick "$mac"
|
||||||
|
done
|
||||||
|
echo; echo "Gesetzt. Kontrolle in ~6 s: bash $0 verify" ;;
|
||||||
|
off)
|
||||||
|
aufraeumen_alle; echo "Alle ${TAG_PREFIX}-* entfernt." ;;
|
||||||
|
verify)
|
||||||
|
lade_karte
|
||||||
|
echo "--- aktive ${TAG_PREFIX}-Regeln ---"
|
||||||
|
r="$(ros "/interface wifi access-list print terse where comment~\"$TAG_PREFIX\"")"
|
||||||
|
[[ -z "${r//[[:space:]]/}" ]] && echo " (keine)" || echo "$r" | tr -d '\r' | sed 's/^/ /'
|
||||||
|
echo "--- aktuelle Anmeldungen (Ziel-Kontrolle) ---"
|
||||||
|
a="$(ros '/interface wifi registration-table print terse proplist=mac-address,interface,signal,ssid')"
|
||||||
|
echo "$a" | tr -d '\r' | grep -E 'mac-address=' | while IFS= read -r line; do
|
||||||
|
ifc="$(sed -n 's/.*interface=\([^ ]*\).*/\1/p' <<< "$line")"
|
||||||
|
echo " ${line} [$(label_of "$ifc")]"
|
||||||
|
done ;;
|
||||||
|
map) bash "$HERE/tp25-pin-s22.sh" map ;;
|
||||||
|
*) echo "Aufruf: $0 {set|verify|off|map} (set liest TP25_PINSPEC)" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
@@ -56,8 +56,14 @@ ros() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# cap-wifiN -> "apX Band" -- live, ohne Cache. Ausgabe je Zeile: cap-wifi3=ap1/5GHz
|
# cap-wifiN -> "apX Band" -- live, ohne Cache. Ausgabe je Zeile: cap-wifi3=ap1/5GHz
|
||||||
|
# 🔴 channel.frequency ist seit dem DFS-Kanalplan (2026-09-03, Frequenz-Listen 5260,5280 /
|
||||||
|
# 5300,5320) fuer die 5G-Radios ein ARRAY, kein Skalar. Die alte Zeile verglich das Array
|
||||||
|
# direkt mit 3000 und lieferte fuer die betroffenen Radios Muell -- den `grep '^cap-wifi'`
|
||||||
|
# samt pipefail dann verschluckte, sodass `map` LEER zurueckkam und ap-map.txt leer blieb.
|
||||||
|
# Fix: bei :typeof=array das erste Element nehmen. Beleg-Kommando dieses Fundes am Manager:
|
||||||
|
# `:foreach ... :put (name . " freq=" . channel.frequency)` -> "5260;5280" fuer cap-wifi1.
|
||||||
karte() {
|
karte() {
|
||||||
ros ':foreach i in=[/interface wifi find where !master-interface] do={:local c [/interface wifi get $i cap]; :local f [/interface wifi get $i channel.frequency]; :local b "2,4GHz"; :if ($f > 3000) do={:set b "5GHz"}; :put ([/interface wifi get $i name] . "=" . [:pick $c 0 [:find $c "@"]] . "/" . $b)}' \
|
ros ':foreach i in=[/interface wifi find where !master-interface] do={:local c [/interface wifi get $i cap]; :local fl [/interface wifi get $i channel.frequency]; :local f 0; :if ([:typeof $fl]="array") do={:set f [:tonum [:pick $fl 0]]} else={:set f [:tonum $fl]}; :local b "2,4GHz"; :if ($f > 3000) do={:set b "5GHz"}; :put ([/interface wifi get $i name] . "=" . [:pick $c 0 [:find $c "@"]] . "/" . $b)}' \
|
||||||
| tr -d '\r' | grep '^cap-wifi[0-9]*='
|
| tr -d '\r' | grep '^cap-wifi[0-9]*='
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ cd "$HERE"
|
|||||||
|
|
||||||
NAME="${1:-$(date +%Y%m%d-%H%M)}"
|
NAME="${1:-$(date +%Y%m%d-%H%M)}"
|
||||||
export TP6_RUN_DIR="$HERE/docs/messdaten/tp25-runde-$NAME"
|
export TP6_RUN_DIR="$HERE/docs/messdaten/tp25-runde-$NAME"
|
||||||
export TP25_PHONE_IP="${TP25_PHONE_IP:-10.40.10.178}" # S22 -- wird gepingt
|
# N-Geraete-Liste (name|ip|mac;...). Default = die bisherigen zwei, damit alte Aufrufe
|
||||||
export TP25_PHONE_MAC="${TP25_PHONE_MAC:-2A:0D:B0:5D:AD:A7}"
|
# unveraendert weiterlaufen. Wird an tp25-walk-messung.sh durchgereicht.
|
||||||
export TP25_IPHONE_MAC="${TP25_IPHONE_MAC:-F2:62:4C:E1:AF:09}"
|
export TP25_PHONES="${TP25_PHONES:-iphone|10.40.10.196|F2:62:4C:E1:AF:09;s22|10.40.10.178|2A:0D:B0:5D:AD:A7}"
|
||||||
|
|
||||||
mkdir -p "$TP6_RUN_DIR"
|
mkdir -p "$TP6_RUN_DIR"
|
||||||
|
|
||||||
@@ -36,14 +36,6 @@ else
|
|||||||
sleep 2
|
sleep 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 🔴 Gegen-AP-Automatik gleich einschalten. Sie war bisher ein Klick auf der Konsole -- und
|
|
||||||
# wer ihn vergisst, geht die ganze Runde mit beiden Telefonen auf DEMSELBEN AP und merkt
|
|
||||||
# es erst bei der Auswertung (im Test am 2026-08-22 genau so passiert). Abschalten geht
|
|
||||||
# weiter jederzeit ueber den Knopf.
|
|
||||||
curl -s -X POST --max-time 10 "http://127.0.0.1:3031/auto?an=1" >/dev/null 2>&1 \
|
|
||||||
&& echo "Gegen-AP-Automatik: EIN" \
|
|
||||||
|| echo "⚠️ Automatik konnte nicht eingeschaltet werden -- auf der Konsole nachholen!"
|
|
||||||
|
|
||||||
echo
|
echo
|
||||||
echo "════════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════════"
|
||||||
echo " Konsole am Telefon oeffnen:"
|
echo " Konsole am Telefon oeffnen:"
|
||||||
@@ -53,8 +45,8 @@ ifconfig | awk '/^[a-z0-9]+:/{i=substr($1,1,length($1)-1)}
|
|||||||
/inet /{if ($2 !~ /^127\.|^169\.254\./) printf " http://%s:3031/ (%s)\n", $2, i}'
|
/inet /{if ($2 !~ /^127\.|^169\.254\./) printf " http://%s:3031/ (%s)\n", $2, i}'
|
||||||
echo
|
echo
|
||||||
echo " 1) Auf dem Telefon einmal irgendwo tippen -> schaltet den Signalton frei"
|
echo " 1) Auf dem Telefon einmal irgendwo tippen -> schaltet den Signalton frei"
|
||||||
echo " 2) Gegen-AP-Automatik laeuft bereits -> S22 folgt dem iPhone spiegelverkehrt"
|
echo " 2) In der Konsole pinnen (Multi-Pin) -> je Client AP/Band waehlen, 'Pin setzen'"
|
||||||
echo " 3) Losgehen. Doppelton = beide angemeldet -> Raum antippen, weiter"
|
echo " 3) Losgehen. Raum antippen, weiter -> Alarm = gepinnter Client haengt nirgends"
|
||||||
echo
|
echo
|
||||||
echo " Lauf: $TP6_RUN_DIR"
|
echo " Lauf: $TP6_RUN_DIR"
|
||||||
echo "════════════════════════════════════════════════════════════"
|
echo "════════════════════════════════════════════════════════════"
|
||||||
@@ -63,18 +55,17 @@ echo
|
|||||||
aufraeumen() {
|
aufraeumen() {
|
||||||
echo
|
echo
|
||||||
echo "Runde beendet. Aufraeumen …"
|
echo "Runde beendet. Aufraeumen …"
|
||||||
# 🔴 REIHENFOLGE: erst die Konsole toeten, dann den Pin loesen. Andersherum ist es ein
|
# 🔴 REIHENFOLGE: erst die Konsole toeten, dann den Pin loesen. Die Konsole pinnt nicht
|
||||||
# Wettlauf, den man verliert -- die Automatik der noch laufenden Konsole sieht den
|
# mehr automatisch (die alte Gegen-AP-Automatik ist mit dem Multi-Pin-Umbau 2026-09-04
|
||||||
# geloesten Pin und setzt ihn binnen zwei Sekunden wieder. Am 2026-08-22 meldete der
|
# entfallen), aber sie haelt ggf. noch SSH-Sitzungen zum Manager offen -- die erst
|
||||||
# Aufraeumer brav "AP-Pin geloest", und danach standen die Regeln trotzdem am Core:
|
# beenden, bevor der Pin geloest wird.
|
||||||
# das Telefon waere nach jeder Runde gepinnt geblieben.
|
|
||||||
[[ -n "$KONSOLE" ]] && kill "$KONSOLE" 2>/dev/null && echo " Konsole beendet."
|
[[ -n "$KONSOLE" ]] && kill "$KONSOLE" 2>/dev/null && echo " Konsole beendet."
|
||||||
# Muster bewusst ohne -e/-p: es gab beide Varianten, und die enge Fassung liess den
|
# Muster bewusst ohne -e/-p: es gab beide Varianten, und die enge Fassung liess den
|
||||||
# Sampler stehen.
|
# Sampler stehen.
|
||||||
pkill -f 'sshpass.*10.40.99.1' 2>/dev/null
|
pkill -f 'sshpass.*10.40.99.1' 2>/dev/null
|
||||||
pkill -f 'tp6-monitor-core.sh' 2>/dev/null
|
pkill -f 'tp6-monitor-core.sh' 2>/dev/null
|
||||||
sleep 1
|
sleep 1
|
||||||
bash docs/tp25-pin-s22.sh off >/dev/null 2>&1 && echo " AP-Pin geloest."
|
bash docs/tp25-pin-multi.sh off >/dev/null 2>&1 && echo " Multi-Pin geloest."
|
||||||
echo
|
echo
|
||||||
echo "Auswertung:"
|
echo "Auswertung:"
|
||||||
echo " python3 docs/tp25-gehweg-auswertung.py --dir $TP6_RUN_DIR"
|
echo " python3 docs/tp25-gehweg-auswertung.py --dir $TP6_RUN_DIR"
|
||||||
|
|||||||
@@ -19,14 +19,24 @@
|
|||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
RUN_DIR="${TP6_RUN_DIR:-/private/tmp/claude-501/-Users-christophemsenhuber-dev-bf-network-r6/0d86ebd1-035f-4513-a231-5cca8111ecfd/scratchpad/tp6-run2}"
|
RUN_DIR="${TP6_RUN_DIR:-/private/tmp/claude-501/-Users-christophemsenhuber-dev-bf-network-r6/0d86ebd1-035f-4513-a231-5cca8111ecfd/scratchpad/tp6-run2}"
|
||||||
PHONE="${TP25_PHONE_IP:-10.40.10.178}" # S22 -- wird gepingt und gesampelt
|
|
||||||
PHONE_MAC="${TP25_PHONE_MAC:-2A:0D:B0:5D:AD:A7}"
|
# 🔴 Handy-Liste generalisiert (2026-09-03, TP25-4-Handy-Messung): statt zwei hartkodierter
|
||||||
# 🔴 Seit dem Lauf vom 2026-08-22 wird das ZWEITE Telefon mitgepingt. Vorher lief der Ping
|
# Telefone eine Liste. Format: "name|ip|mac;name|ip|mac;..." -- '|' als Feldtrenner (die MAC
|
||||||
# nur auf das gepinnte Geraet -- und weil das per Automatik immer auf dem GEGEN-AP sitzt,
|
# enthaelt selbst ':'), ';' als Listentrenner. Pro Handy laeuft EIN eigener Sampler
|
||||||
# fehlte ausgerechnet die Strecke, die die Alltagsabdeckung zeigt. Der Terrassen-Vergleich
|
# (sampler-<name>.csv) UND ein eigener Core-Ping (walk-ping-<name>.csv). Default = die
|
||||||
# liess sich deshalb nicht fuehren, obwohl beide Telefone dabei waren.
|
# bisherigen zwei, damit alte Aufrufe unveraendert weiterlaufen.
|
||||||
PHONE2="${TP25_PHONE2_IP:-10.40.10.196}" # iPhone -- roamt frei
|
# Der Core-Ping (5/s) erzeugt zugleich den Dauer-Downlink, der die Taschen-Handys wach haelt.
|
||||||
PHONE2_NAME="${TP25_PHONE2_NAME:-iphone}"
|
TP25_PHONES="${TP25_PHONES:-iphone|10.40.10.196|F2:62:4C:E1:AF:09;s22|10.40.10.178|2A:0D:B0:5D:AD:A7}"
|
||||||
|
declare -a PH_NAME PH_IP PH_MAC
|
||||||
|
IFS=';' read -ra _ENTRIES <<< "$TP25_PHONES"
|
||||||
|
for _e in "${_ENTRIES[@]}"; do
|
||||||
|
[[ -z "${_e//[[:space:]]/}" ]] && continue
|
||||||
|
IFS='|' read -r _n _ip _mac <<< "$_e"
|
||||||
|
[[ -n "$_n" && -n "$_ip" && -n "$_mac" ]] || { echo "TP25_PHONES-Eintrag unvollstaendig: '$_e' (erwartet name|ip|mac)" >&2; exit 2; }
|
||||||
|
PH_NAME+=("$_n"); PH_IP+=("$_ip"); PH_MAC+=("$_mac")
|
||||||
|
done
|
||||||
|
[[ ${#PH_NAME[@]} -gt 0 ]] || { echo "TP25_PHONES leer/ungueltig: '$TP25_PHONES'" >&2; exit 2; }
|
||||||
|
|
||||||
STATE="$RUN_DIR/phase.json"
|
STATE="$RUN_DIR/phase.json"
|
||||||
MARKERS="$RUN_DIR/markers.csv"
|
MARKERS="$RUN_DIR/markers.csv"
|
||||||
HERE="$(cd "$(dirname "$0")/.." && pwd)"
|
HERE="$(cd "$(dirname "$0")/.." && pwd)"
|
||||||
@@ -34,6 +44,14 @@ HERE="$(cd "$(dirname "$0")/.." && pwd)"
|
|||||||
mkdir -p "$RUN_DIR"
|
mkdir -p "$RUN_DIR"
|
||||||
[[ -f "$MARKERS" ]] || echo "ts_epoch,ts_local,marker" > "$MARKERS"
|
[[ -f "$MARKERS" ]] || echo "ts_epoch,ts_local,marker" > "$MARKERS"
|
||||||
|
|
||||||
|
# Client-Liste fuer die Klick-Konsole (docs/tp6-marker/server.py). Die Konsole liest sie
|
||||||
|
# ueber den .current-run-Zeiger -- so kennt sie die N Geraete auch, wenn sie als
|
||||||
|
# Dauer-Preview schon vor dem Messlauf lief. Format: name|ip|mac je Zeile.
|
||||||
|
: > "$RUN_DIR/geraete.txt"
|
||||||
|
for i in "${!PH_NAME[@]}"; do
|
||||||
|
printf '%s|%s|%s\n' "${PH_NAME[$i]}" "${PH_IP[$i]}" "${PH_MAC[$i]}" >> "$RUN_DIR/geraete.txt"
|
||||||
|
done
|
||||||
|
|
||||||
# Zeiger fuer die Klick-Konsole (docs/tp6-marker/server.py). Die laeuft als Dauer-Preview
|
# Zeiger fuer die Klick-Konsole (docs/tp6-marker/server.py). Die laeuft als Dauer-Preview
|
||||||
# auf Port 3031 und kennt dieses Verzeichnis sonst nicht -- ihre Marker landeten dann im
|
# auf Port 3031 und kennt dieses Verzeichnis sonst nicht -- ihre Marker landeten dann im
|
||||||
# Lauf von gestern, waehrend der Sampler hierher schreibt. Beides fiel erst bei der
|
# Lauf von gestern, waehrend der Sampler hierher schreibt. Beides fiel erst bei der
|
||||||
@@ -51,10 +69,17 @@ bash "$HERE/docs/tp25-pin-s22.sh" map > "$RUN_DIR/ap-map.txt" 2>/dev/null \
|
|||||||
|| echo "⚠️ AP-Zuordnung konnte nicht gelesen werden -- Auswertung bleibt bei cap-wifiN"
|
|| echo "⚠️ AP-Zuordnung konnte nicht gelesen werden -- Auswertung bleibt bei cap-wifiN"
|
||||||
|
|
||||||
# Frueheren Lauf beiseitelegen, statt ihn zu ueberschreiben.
|
# Frueheren Lauf beiseitelegen, statt ihn zu ueberschreiben.
|
||||||
if [[ -f "$RUN_DIR/sampler.csv" ]]; then
|
# 🔴 Seit dem N-Geraete-Umbau (2026-09-04) schreiben neue Laeufe pro Geraet
|
||||||
|
# sampler-<name>.csv / walk-ping-<name>.csv -- der alte Trigger auf die nackte sampler.csv
|
||||||
|
# haette einen Re-Run nicht mehr erkannt und den Vorlauf ueberschrieben. Legacy-Namen
|
||||||
|
# (sampler.csv/walk-ping.csv/beide-telefone.csv aus 2-Rollen-Laeufen) bleiben in der Liste.
|
||||||
|
if compgen -G "$RUN_DIR/sampler-*.csv" >/dev/null || compgen -G "$RUN_DIR/walk-ping-*.csv" >/dev/null \
|
||||||
|
|| [[ -f "$RUN_DIR/sampler.csv" ]]; then
|
||||||
ARCHIV="$RUN_DIR/archiv-$(date +%H%M%S)"; mkdir -p "$ARCHIV"
|
ARCHIV="$RUN_DIR/archiv-$(date +%H%M%S)"; mkdir -p "$ARCHIV"
|
||||||
for f in sampler.csv markers.csv walk-ping.csv walk-ping.log ping-dauerlauf.log; do
|
for f in "$RUN_DIR"/sampler.csv "$RUN_DIR"/sampler-*.csv "$RUN_DIR"/sampler-*.out \
|
||||||
[[ -f "$RUN_DIR/$f" ]] && mv "$RUN_DIR/$f" "$ARCHIV/"
|
"$RUN_DIR"/markers.csv "$RUN_DIR"/walk-ping.csv "$RUN_DIR"/walk-ping-*.csv \
|
||||||
|
"$RUN_DIR"/walk-ping.log "$RUN_DIR"/ping-dauerlauf.log "$RUN_DIR"/beide-telefone.csv; do
|
||||||
|
[[ -f "$f" ]] && mv "$f" "$ARCHIV/"
|
||||||
done
|
done
|
||||||
echo "ts_epoch,ts_local,marker" > "$MARKERS"
|
echo "ts_epoch,ts_local,marker" > "$MARKERS"
|
||||||
echo "Vorheriger Lauf -> $ARCHIV"
|
echo "Vorheriger Lauf -> $ARCHIV"
|
||||||
@@ -74,9 +99,16 @@ PY
|
|||||||
|
|
||||||
state "Kein Call nötig. <b>🚶</b> drücken, wenn du losgehst, <b>📍</b> wenn du stehen bleibst — und bitte den Weg gehen, den du beim Telefonieren wirklich gehst.<br><br>Ich pinge dein Telefon fünfmal pro Sekunde. Jeder AP- oder Bandwechsel zeigt sich dann als echter Paketverlust: <b>so lange ist der Call wirklich tot.</b><br><br>Am Ende: <b>⏹ Aufzeichnung stoppen</b>."
|
state "Kein Call nötig. <b>🚶</b> drücken, wenn du losgehst, <b>📍</b> wenn du stehen bleibst — und bitte den Weg gehen, den du beim Telefonieren wirklich gehst.<br><br>Ich pinge dein Telefon fünfmal pro Sekunde. Jeder AP- oder Bandwechsel zeigt sich dann als echter Paketverlust: <b>so lange ist der Call wirklich tot.</b><br><br>Am Ende: <b>⏹ Aufzeichnung stoppen</b>."
|
||||||
|
|
||||||
# Sampler am Manager (1-s-Takt, feiner als bei der TP6-Messung)
|
# Sampler am Manager -- pro Geraet einer. 2-s-Takt: bei 4 Geraeten laufen sonst 8 SSH-Sessions
|
||||||
bash "$HERE/docs/tp6-monitor-core.sh" --out "$RUN_DIR/sampler.csv" --mac "$PHONE_MAC" --interval 1 \
|
# gegen den Core-SPOF, und Signal alle 2 s reicht fuer einen Gehweg (der Ping bleibt fein bei
|
||||||
> "$RUN_DIR/sampler.out" 2>&1 & SAMPLER=$!
|
# 200 ms). Jeder Sampler holt sich den Core-PW selbst (docs/tp6-monitor-core.sh, SSHPASS -e).
|
||||||
|
declare -a SAMPLERS
|
||||||
|
for i in "${!PH_NAME[@]}"; do
|
||||||
|
bash "$HERE/docs/tp6-monitor-core.sh" --out "$RUN_DIR/sampler-${PH_NAME[$i]}.csv" \
|
||||||
|
--mac "${PH_MAC[$i]}" --interval 2 \
|
||||||
|
> "$RUN_DIR/sampler-${PH_NAME[$i]}.out" 2>&1 &
|
||||||
|
SAMPLERS+=("$!")
|
||||||
|
done
|
||||||
|
|
||||||
# 5 Pings/s auf JEDES Telefon — GEPINGT WIRD VOM CORE, nicht von diesem Rechner.
|
# 5 Pings/s auf JEDES Telefon — GEPINGT WIRD VOM CORE, nicht von diesem Rechner.
|
||||||
# 🔴 Der Grund: pingt der Laptop, laeuft der Weg ueber ZWEI Funkstrecken (Laptop -> AP ->
|
# 🔴 Der Grund: pingt der Laptop, laeuft der Weg ueber ZWEI Funkstrecken (Laptop -> AP ->
|
||||||
@@ -124,8 +156,11 @@ ping_lauf() {
|
|||||||
# stdout-Pipe schliesst, und der Hintergrundjob darin haelt sie offen -- das Skript blieb
|
# stdout-Pipe schliesst, und der Hintergrundjob darin haelt sie offen -- das Skript blieb
|
||||||
# dort stehen, der zweite Ping wurde nie gestartet (2026-08-22). Die Funktion laeuft in
|
# dort stehen, der zweite Ping wurde nie gestartet (2026-08-22). Die Funktion laeuft in
|
||||||
# der aktuellen Shell, also liefert $! danach die richtige PID.
|
# der aktuellen Shell, also liefert $! danach die richtige PID.
|
||||||
ping_lauf "$PHONE" "$RUN_DIR/walk-ping.csv"; PINGER=$!
|
declare -a PINGERS
|
||||||
ping_lauf "$PHONE2" "$RUN_DIR/walk-ping-$PHONE2_NAME.csv"; PINGER2=$!
|
for i in "${!PH_NAME[@]}"; do
|
||||||
|
ping_lauf "${PH_IP[$i]}" "$RUN_DIR/walk-ping-${PH_NAME[$i]}.csv"
|
||||||
|
PINGERS+=("$!")
|
||||||
|
done
|
||||||
|
|
||||||
# 🔴 Kinder ZUERST. `kill $SAMPLER` traf nur den Wrapper; dessen Subshell samt SSH-Sitzung
|
# 🔴 Kinder ZUERST. `kill $SAMPLER` traf nur den Wrapper; dessen Subshell samt SSH-Sitzung
|
||||||
# wurde dabei verwaist und lief weiter -- nach dem Stopp hielt der Core stumm eine
|
# wurde dabei verwaist und lief weiter -- nach dem Stopp hielt der Core stumm eine
|
||||||
@@ -138,9 +173,8 @@ killbaum() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
beenden() {
|
beenden() {
|
||||||
for pid in $SAMPLER $PINGER $PINGER2; do killbaum "$pid"; done
|
for pid in "${SAMPLERS[@]}" "${PINGERS[@]}"; do killbaum "$pid"; done
|
||||||
pkill -f "ping $PHONE interval=200ms" 2>/dev/null
|
for ip in "${PH_IP[@]}"; do pkill -f "ping $ip interval=200ms" 2>/dev/null; done
|
||||||
pkill -f "ping $PHONE2 interval=200ms" 2>/dev/null
|
|
||||||
# Der /ping am Router endet mit der SSH-Sitzung (am 2026-08-22 nachgeprueft: nach dem
|
# Der /ping am Router endet mit der SSH-Sitzung (am 2026-08-22 nachgeprueft: nach dem
|
||||||
# Kill der Sitzungen lief am Core nichts weiter). Bewusst KEIN "job remove" am Geraet --
|
# Kill der Sitzungen lief am Core nichts weiter). Bewusst KEIN "job remove" am Geraet --
|
||||||
# ein pauschales Aufraeumen fremder Jobs waere ein zu grobes Werkzeug fuer ein Messskript.
|
# ein pauschales Aufraeumen fremder Jobs waere ein zu grobes Werkzeug fuer ein Messskript.
|
||||||
@@ -150,10 +184,12 @@ beenden() {
|
|||||||
}
|
}
|
||||||
trap beenden EXIT
|
trap beenden EXIT
|
||||||
|
|
||||||
echo "Gehweg-Messung laeuft."
|
echo "Gehweg-Messung laeuft (${#PH_NAME[@]} Geraete, alle frei roamend)."
|
||||||
echo " Funk -> $RUN_DIR/sampler.csv"
|
for i in "${!PH_NAME[@]}"; do
|
||||||
echo " Ping -> $RUN_DIR/walk-ping.csv ($PHONE, gepinnt)"
|
printf ' %-9s Funk -> sampler-%s.csv Ping -> walk-ping-%s.csv (%s)\n' \
|
||||||
echo " Ping -> $RUN_DIR/walk-ping-$PHONE2_NAME.csv ($PHONE2, frei roamend)"
|
"${PH_NAME[$i]}" "${PH_NAME[$i]}" "${PH_NAME[$i]}" "${PH_IP[$i]}"
|
||||||
|
done
|
||||||
|
echo " Verzeichnis: $RUN_DIR"
|
||||||
echo " Stoppen: '⏹ Aufzeichnung stoppen' auf der Konsole -- oder Ctrl-C hier."
|
echo " Stoppen: '⏹ Aufzeichnung stoppen' auf der Konsole -- oder Ctrl-C hier."
|
||||||
|
|
||||||
# 🔴 Bis 2026-08-22 lief hier nur `while true; do touch; sleep 5; done`, und das Skript war
|
# 🔴 Bis 2026-08-22 lief hier nur `while true; do touch; sleep 5; done`, und das Skript war
|
||||||
|
|||||||
38
docs/tp41-t5-idp-callback-anleitung.md
Normal file
38
docs/tp41-t5-idp-callback-anleitung.md
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# TP41 T5 — IdP-Callback-URLs reparieren (der Daily-OTP-Fix)
|
||||||
|
|
||||||
|
**Problem:** Nach dem Team-Domain-Rename `bf4` → `emsenhuber` kennen die OAuth-Apps bei GitHub/Google
|
||||||
|
die neue Callback-URL nicht. Login schlägt mit „redirect_uri is not associated with this application"
|
||||||
|
fehl → Cloudflare Access fällt auf **E-Mail-OTP** zurück (das tägliche Nervthema).
|
||||||
|
|
||||||
|
**Die EINE URL**, die bei beiden Providern hinterlegt sein muss:
|
||||||
|
`https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/callback`
|
||||||
|
|
||||||
|
## GitHub (Client ID `Ov23li2h96hAkGRPJXYV`)
|
||||||
|
1. github.com → rechts oben aufs Profilbild → **Settings**.
|
||||||
|
2. Linke Spalte ganz unten **Developer settings** → **OAuth Apps**.
|
||||||
|
3. Die App mit Client ID `Ov23li2h96hAkGRPJXYV` öffnen.
|
||||||
|
4. **Authorization callback URL** = `https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/callback`.
|
||||||
|
5. (Optional **Homepage URL** = `https://emsenhuber.cloudflareaccess.com`.)
|
||||||
|
6. **Update application** klicken.
|
||||||
|
|
||||||
|
## Google (Client ID `276709275903-b3q55lsj203v4strbhh5mub76cib867e.apps.googleusercontent.com`)
|
||||||
|
1. console.cloud.google.com → oben das richtige **Projekt** wählen.
|
||||||
|
2. **APIs & Services** → **Credentials**.
|
||||||
|
3. Unter **OAuth 2.0 Client IDs** den Client `276709275903-…apps.googleusercontent.com`
|
||||||
|
(Typ *Web application*) öffnen.
|
||||||
|
4. **Authorized redirect URIs** → **+ ADD URI** →
|
||||||
|
`https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/callback`.
|
||||||
|
5. **Authorized JavaScript origins** → **+ ADD URI** → `https://emsenhuber.cloudflareaccess.com`.
|
||||||
|
6. **SAVE**. (Google-Propagation dauert teils ein paar Minuten.)
|
||||||
|
|
||||||
|
## Testen
|
||||||
|
1. Neues **privates** Browserfenster → eine Access-geschützte Seite öffnen.
|
||||||
|
2. Beim CF-Login **GitHub** bzw. **Google** wählen → muss ohne „redirect_uri"-Fehler durchlaufen,
|
||||||
|
**keine** OTP-Mail mehr.
|
||||||
|
3. Gegenprobe (optional, kann Claude read-only): CF-Audit
|
||||||
|
`GET /accounts/<acc>/access/logs/access_requests` → `identity_provider` = `github`/`google`
|
||||||
|
statt `onetimepin`.
|
||||||
|
|
||||||
|
## Hinweis
|
||||||
|
Die Team-Domain heißt `emsenhuber.cloudflareaccess.com` (das alte Org-Label `bf4…` in CF nicht
|
||||||
|
anfassen). Session gilt 720 h (30 Tage) — nach dem Fix sollte der Login also lange halten.
|
||||||
@@ -16,12 +16,12 @@ Zwei Aufgaben:
|
|||||||
- Die Seite hat ein <meta refresh>. Ohne JS aktualisiert sich die Anleitung dadurch
|
- Die Seite hat ein <meta refresh>. Ohne JS aktualisiert sich die Anleitung dadurch
|
||||||
trotzdem; laeuft JS, entfernt es das Meta-Tag und pollt stattdessen leise.
|
trotzdem; laeuft JS, entfernt es das Meta-Tag und pollt stattdessen leise.
|
||||||
|
|
||||||
🔴 SEIT 2026-08-22 AENDERT DIESE SEITE DOCH ETWAS AM NETZ: der AP-Pin (TP25) schreibt
|
🔴 SEIT 2026-08-22 AENDERT DIESE SEITE DOCH ETWAS AM NETZ: der Multi-Pin (TP25) schreibt
|
||||||
access-list-Regeln auf den WLAN-Manager. Die Seite bindet auf 0.0.0.0 und kennt keine
|
access-list-Regeln auf den WLAN-Manager. Die Seite bindet auf 0.0.0.0 und kennt keine
|
||||||
Authentifizierung — wer in VLAN10 steht, kann den Pin umschalten. Das ist fuer die Dauer
|
Authentifizierung — wer in VLAN10 steht, kann pinnen. Das ist fuer die Dauer einer Messung
|
||||||
einer Messung vertretbar und sonst nicht: den Server nach dem Rundgang beenden. Der Pin
|
vertretbar und sonst nicht: den Server nach dem Rundgang beenden. Der Pin selbst ist
|
||||||
selbst ist harmlos genug (er betrifft eine MAC), aber die Regel ist es nicht, die als
|
harmlos genug (er betrifft MACs), aber die Regel ist es nicht, die als naechstes hier
|
||||||
naechstes hier landen koennte.
|
landen koennte.
|
||||||
|
|
||||||
Bindet auf 0.0.0.0 (Bedienung vom iPhone/iPad).
|
Bindet auf 0.0.0.0 (Bedienung vom iPhone/iPad).
|
||||||
"""
|
"""
|
||||||
@@ -164,6 +164,82 @@ def sampler_tail():
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def geraete():
|
||||||
|
"""Liste (name, ip, mac) aus geraete.txt im Laufverzeichnis.
|
||||||
|
|
||||||
|
🔴 Die Datei schreibt tp25-walk-messung.sh beim Start des Messlaufs (eine Zeile
|
||||||
|
name|ip|mac je Client). Fehlt sie — Alt-Lauf oder Dauer-Preview, die schon vor dem
|
||||||
|
ersten Lauf lief — fallen wir auf die zwei bisherigen Geraete zurueck, damit die
|
||||||
|
Anzeige nicht leer ist. Die Namen muessen zu den sampler-<name>.csv /
|
||||||
|
walk-ping-<name>.csv passen, die die Harness schreibt.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
zeilen = [z.strip() for z in open(_p("geraete.txt")) if z.strip()]
|
||||||
|
gs = []
|
||||||
|
for z in zeilen:
|
||||||
|
teile = z.split("|")
|
||||||
|
if len(teile) >= 3 and teile[0]:
|
||||||
|
gs.append((teile[0], teile[1], teile[2]))
|
||||||
|
if gs:
|
||||||
|
return gs
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return [("iphone", "", "F2:62:4C:E1:AF:09"), ("s22", "", "2A:0D:B0:5D:AD:A7")]
|
||||||
|
|
||||||
|
|
||||||
|
def client_tail(name):
|
||||||
|
"""Letzte Sampler-Zeile EINES Geraets (sampler-<name>.csv). Fehlt sie, ist das kein Fehler.
|
||||||
|
|
||||||
|
Gleiche Spalten wie sampler_tail() — die Harness schreibt pro Geraet einen eigenen
|
||||||
|
Sampler mit demselben Header (tp6-monitor-core.sh). Die Konsole braucht deshalb KEINE
|
||||||
|
eigene SSH-Sitzung mehr: sie liest nur, was die Sampler schon auf die Platte legen.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
pfad = _p("sampler-%s.csv" % name)
|
||||||
|
rows = [r.strip() for r in open(pfad) if r.strip()]
|
||||||
|
if len(rows) < 2:
|
||||||
|
return {}
|
||||||
|
row = dict(zip(rows[0].split(","), rows[-1].split(",")))
|
||||||
|
return {
|
||||||
|
"age": int(time.time() - int(row.get("ts_epoch", "0"))),
|
||||||
|
"cpu": row.get("cpu", ""), "wan_rx": row.get("wan_rx_bps", ""),
|
||||||
|
"wan_tx": row.get("wan_tx_bps", ""), "iface": row.get("sta_iface", ""),
|
||||||
|
"signal": row.get("sta_signal", ""), "tx_rate": row.get("sta_tx_rate", ""),
|
||||||
|
"sta_tx_bps": row.get("sta_tx_bps", ""), "sta_rx_bps": row.get("sta_rx_bps", ""),
|
||||||
|
}
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def ping_loss(name, n=150):
|
||||||
|
"""Ping-Verlust der letzten ~n Zeilen von walk-ping-<name>.csv (5 Pings/s -> n=150 ~ 30 s).
|
||||||
|
|
||||||
|
Leeres ms-Feld = Verlust. Liefert (verloren, gesamt) oder None, wenn noch keine Daten da
|
||||||
|
sind. Der Header (ts_local,seq,ms) wird ueber die nicht-numerische seq-Spalte uebersprungen.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
rows = [r.strip() for r in open(_p("walk-ping-%s.csv" % name)) if r.strip()]
|
||||||
|
verloren = gesamt = 0
|
||||||
|
for z in rows[-n:]:
|
||||||
|
teile = z.split(",")
|
||||||
|
if len(teile) >= 3 and teile[1].isdigit():
|
||||||
|
gesamt += 1
|
||||||
|
if not teile[2].strip():
|
||||||
|
verloren += 1
|
||||||
|
return (verloren, gesamt) if gesamt else None
|
||||||
|
except Exception:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def wan_tail():
|
||||||
|
"""WAN/CPU-Anzeige: aus sampler.csv (Alt-Lauf) oder dem ersten Geraet-Sampler."""
|
||||||
|
w = sampler_tail()
|
||||||
|
if w.get("cpu") or w.get("wan_rx"):
|
||||||
|
return w
|
||||||
|
gs = geraete()
|
||||||
|
return client_tail(gs[0][0]) if gs else {}
|
||||||
|
|
||||||
|
|
||||||
def kill_flows():
|
def kill_flows():
|
||||||
"""Not-Aus: Saettigungs-Flows sofort beenden."""
|
"""Not-Aus: Saettigungs-Flows sofort beenden."""
|
||||||
killed = 0
|
killed = 0
|
||||||
@@ -214,153 +290,104 @@ def ap_map():
|
|||||||
return _apmap["karte"]
|
return _apmap["karte"]
|
||||||
|
|
||||||
|
|
||||||
# 🔎 Die Messidee (Christoph, 2026-08-22): das iPhone roamt frei und zeigt damit, welchen AP
|
# 🔎 Die Messidee (Christoph, 2026-08-22, generalisiert 2026-09-04): die Clients roamen frei
|
||||||
# ein normaler Client an dieser Stelle waehlt -- das wissen wir also ohnehin. Interessant
|
# und zeigen damit, welchen AP ein normaler Client an dieser Stelle waehlt. Fuer die
|
||||||
# ist der ANDERE. Deshalb wird das S22 automatisch auf den Gegen-AP gezwungen: pro Standort
|
# Abdeckungsfrage wird je Client per access-list GENAU EIN AP+Band erlaubt
|
||||||
# gibt es dann zwei Messpunkte statt einem, ohne zweimal zu gehen.
|
# (tp25-pin-multi.sh). Die alte Gegen-AP-Automatik (S22 folgt dem iPhone) ist damit
|
||||||
IPHONE_MAC = os.environ.get("TP25_IPHONE_MAC", "F2:62:4C:E1:AF:09")
|
# ueberholt: gepinnt wird vorab band-genau, nicht mehr automatisch waehrend des Gehens.
|
||||||
S22_MAC = os.environ.get("TP25_PIN_MAC", "2A:0D:B0:5D:AD:A7")
|
|
||||||
|
|
||||||
# Was der Beobachter sieht. "" = nicht angemeldet.
|
PINM_SH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "tp25-pin-multi.sh")
|
||||||
_live = {"iphone": "", "iphone_sig": "", "s22": "", "s22_sig": "", "stand": 0.0}
|
|
||||||
_auto = {"an": False, "seit_weg": 0.0, "aufgegeben": ""}
|
# Pin-Zustand. Das Setzen dauert ~8 s (Regeln + Kick), deshalb laeuft es in einem Thread und
|
||||||
|
# die Seite zeigt solange "setze Pin …". pins = {name: "ap|band"} der zuletzt gesetzten Auswahl.
|
||||||
|
_pinm = {"busy": False, "msg": "", "pins": {}, "stand": 0.0}
|
||||||
|
|
||||||
|
|
||||||
def core_passwort():
|
def pin_targets():
|
||||||
"""Aus dem Credential-Store. Wird nie geloggt und nie in die Seite gerendert."""
|
"""Eindeutige (ap, band)-Paare aus der Live-Karte, band-normalisiert fuer tp25-pin-multi.sh.
|
||||||
with open(os.path.expanduser("~/.secrets/credentials.env")) as fh:
|
|
||||||
for zeile in fh:
|
|
||||||
if zeile.startswith("CRS326_CORE_PASS="):
|
|
||||||
return zeile.split("=", 1)[1].strip().strip("\"'")
|
|
||||||
raise RuntimeError("CRS326_CORE_PASS nicht gefunden")
|
|
||||||
|
|
||||||
|
ap_map() liefert 'apX / 5 GHz' bzw. 'apX / 2,4 GHz'; das Skript erwartet Band '5' oder '24'.
|
||||||
def _ap_von(iface):
|
|
||||||
return ap_map().get(iface, "").split(" /")[0]
|
|
||||||
|
|
||||||
|
|
||||||
def beobachter():
|
|
||||||
"""Haelt EINE SSH-Sitzung offen und liest beide Telefone im 2-s-Takt.
|
|
||||||
|
|
||||||
🔴 Bewusst kein Poll mit je einem ssh-Aufruf: das waeren ueber einen Rundgang mehrere
|
|
||||||
hundert Verbindungsaufbauten, jeder ~1 s. Dasselbe Muster wie tp6-monitor-core.sh --
|
|
||||||
inklusive Reconnect-Schleife, weil die Sitzung am 2026-08-21 mitten in einer Messung
|
|
||||||
weggebrochen ist und die Daten still endeten.
|
|
||||||
"""
|
"""
|
||||||
# 🔴 KEINE Klammern um die :while-Schleife -- "(:while ...)" laesst RouterOS die
|
ziele, gesehen = [], set()
|
||||||
# Sitzung wortlos beenden, und mit stderr auf DEVNULL sieht man nur, dass alle 3 s
|
for wert in ap_map().values():
|
||||||
# ein neuer ssh startet. Genau so lag der Beobachter am 2026-08-22 tot.
|
ap, _, band = wert.partition(" / ")
|
||||||
ros = (':while (true) do={'
|
band = band.replace(" GHz", "").replace(",", ".")
|
||||||
':local a [/interface/wifi/registration-table find where mac-address="%s"];'
|
if band.startswith("5"):
|
||||||
':local b [/interface/wifi/registration-table find where mac-address="%s"];'
|
band = "5"
|
||||||
':local ai ""; :local as2 ""; :local bi ""; :local bs "";'
|
elif band.startswith("2"):
|
||||||
':if ([:len $a] > 0) do={:set ai [/interface/wifi/registration-table get [:pick $a 0] interface];'
|
band = "24"
|
||||||
':set as2 [/interface/wifi/registration-table get [:pick $a 0] signal]};'
|
else:
|
||||||
':if ([:len $b] > 0) do={:set bi [/interface/wifi/registration-table get [:pick $b 0] interface];'
|
continue
|
||||||
':set bs [/interface/wifi/registration-table get [:pick $b 0] signal]};'
|
if (ap, band) not in gesehen:
|
||||||
':put ($ai . "," . $as2 . "," . $bi . "," . $bs); :delay 2s;}'
|
gesehen.add((ap, band))
|
||||||
% (IPHONE_MAC, S22_MAC))
|
ziele.append((ap, band))
|
||||||
while True:
|
return sorted(ziele)
|
||||||
try:
|
|
||||||
pw = core_passwort()
|
|
||||||
# 🔴 sshpass -e statt -p: bei -p steht das Router-Passwort in der KOMMANDOZEILE
|
|
||||||
# und damit in `ps`, in jedem Prozess-Trace und in jeder Debug-Ausgabe, die
|
|
||||||
# argv anfasst. Am 2026-08-22 ist es genau so im Klartext in einem Trace
|
|
||||||
# gelandet. Mit -e liegt es nur in der Umgebung dieses einen Kindprozesses.
|
|
||||||
umgebung = dict(os.environ, SSHPASS=pw)
|
|
||||||
proc = subprocess.Popen(
|
|
||||||
["sshpass", "-e", "ssh", "-o", "StrictHostKeyChecking=no",
|
|
||||||
"-o", "ConnectTimeout=8", "-o", "ServerAliveInterval=15",
|
|
||||||
"admin@10.40.99.1", ros],
|
|
||||||
stdout=subprocess.PIPE, stderr=open(_p("beobachter.log"), "a"),
|
|
||||||
text=True, bufsize=1, env=umgebung)
|
|
||||||
# 🔴 NICHT `for zeile in proc.stdout`: die Iteration eines Pipes liest blockweise
|
|
||||||
# vor, die Zeilen kommen also erst an, wenn ~4 KB beisammen sind -- bei einer
|
|
||||||
# Zeile alle 2 s waere das eine Stunde. readline() liefert sofort.
|
|
||||||
for zeile in iter(proc.stdout.readline, ""):
|
|
||||||
teile = zeile.strip().split(",")
|
|
||||||
if len(teile) != 4:
|
|
||||||
continue
|
|
||||||
_live.update(iphone=teile[0], iphone_sig=teile[1],
|
|
||||||
s22=teile[2], s22_sig=teile[3], stand=time.time())
|
|
||||||
# 🔴 Bis 2026-08-22 blieben diese Zahlen NUR im Speicher, fuer die Anzeige.
|
|
||||||
# Nach dem Rundgang fehlte deshalb die halbe Messung: das S22 sitzt per
|
|
||||||
# Automatik immer auf dem Gegen-AP, die Alltagsabdeckung zeigt das iPhone
|
|
||||||
# -- und genau dessen Zeitreihe war weg. Nachtraeglich liess sich nur das
|
|
||||||
# Roaming-Log am Manager retten: Ereignisse, keine Kurve.
|
|
||||||
mitschreiben(teile)
|
|
||||||
entscheide()
|
|
||||||
except Exception as e:
|
|
||||||
# Ein Beobachter, der still stirbt, ist schlimmer als keiner (Lehre aus dem
|
|
||||||
# Sampler-Abbruch am 2026-08-21). Also mitschreiben statt schlucken.
|
|
||||||
try:
|
|
||||||
with open(_p("beobachter.log"), "a") as fh:
|
|
||||||
fh.write("%s %s: %s\n" % (time.strftime("%H:%M:%S"), type(e).__name__, e))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
time.sleep(3)
|
|
||||||
|
|
||||||
|
|
||||||
def mitschreiben(teile):
|
def build_spec(form):
|
||||||
"""Beide Telefone im 2-s-Takt in den Lauf schreiben: iface + Signal je Geraet."""
|
"""Aus den Formular-Auswahlen (pin_<name>=ap|band oder frei) den TP25_PINSPEC-String bauen.
|
||||||
try:
|
|
||||||
pfad = _p("beide-telefone.csv")
|
Clients ohne Ziel (frei) bleiben frei — tp25-pin-multi.sh set raeumt vorher alle
|
||||||
frisch = not os.path.exists(pfad) or os.path.getsize(pfad) == 0
|
TP25-PINM-*-Regeln ab und setzt nur die hier genannten.
|
||||||
with open(pfad, "a") as fh:
|
"""
|
||||||
if frisch:
|
eintraege = []
|
||||||
fh.write("ts_epoch,ts_local,iphone_iface,iphone_sig,s22_iface,s22_sig\n")
|
for name, _ip, mac in geraete():
|
||||||
fh.write("%d,%s,%s\n" % (time.time(), time.strftime("%H:%M:%S"), ",".join(teile)))
|
wert = (form.get("pin_%s" % name) or ["frei"])[0]
|
||||||
except Exception:
|
if wert and wert != "frei" and "|" in wert:
|
||||||
pass
|
ap, band = wert.split("|", 1)
|
||||||
|
eintraege.append("%s|%s|%s|%s" % (name, mac, ap, band))
|
||||||
|
return ";".join(eintraege)
|
||||||
|
|
||||||
|
|
||||||
def entscheide():
|
def pinm_aktion(spec):
|
||||||
"""Gegen-AP setzen, sobald das iPhone den AP wechselt."""
|
"""Pin setzen (spec = 'name|mac|ap|band;...') oder loesen (spec = ''). Laeuft im Thread."""
|
||||||
if not _auto["an"] or _pin["busy"]:
|
pins = {}
|
||||||
return
|
for e in (spec.split(";") if spec else []):
|
||||||
ip_ap = _ap_von(_live["iphone"])
|
teile = e.split("|")
|
||||||
if ip_ap not in ("ap1", "ap2"):
|
if len(teile) >= 4:
|
||||||
return # iPhone selbst nicht angemeldet -> nichts zu spiegeln
|
pins[teile[0]] = "%s|%s" % (teile[2], teile[3])
|
||||||
gegen = "ap1" if ip_ap == "ap2" else "ap2"
|
|
||||||
if pin_lesen() != gegen:
|
|
||||||
_auto["aufgegeben"] = ""
|
|
||||||
_auto["seit_weg"] = time.time()
|
|
||||||
pin_setzen(gegen)
|
|
||||||
|
|
||||||
|
|
||||||
# Pin-Zustand. Das Umschalten dauert ~8 s (Regeln + Kick + Nachlesen), deshalb laeuft es
|
|
||||||
# in einem Thread und die Seite zeigt solange "schaltet um".
|
|
||||||
_pin = {"ziel": None, "busy": False, "msg": "", "stand": 0.0}
|
|
||||||
|
|
||||||
|
|
||||||
def pin_lesen():
|
|
||||||
if _pin["busy"] or time.time() - _pin["stand"] < 20:
|
|
||||||
return _pin["ziel"]
|
|
||||||
try:
|
|
||||||
_pin["ziel"] = subprocess.run(["bash", PIN_SH, "which"], capture_output=True,
|
|
||||||
text=True, timeout=20).stdout.strip() or None
|
|
||||||
_pin["stand"] = time.time()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return _pin["ziel"]
|
|
||||||
|
|
||||||
|
|
||||||
def pin_setzen(ziel):
|
|
||||||
def lauf():
|
def lauf():
|
||||||
try:
|
try:
|
||||||
r = subprocess.run(["bash", PIN_SH, ziel], capture_output=True, text=True, timeout=60)
|
umgebung = dict(os.environ)
|
||||||
_pin["msg"] = (r.stdout.strip().splitlines() or [""])[0]
|
if spec:
|
||||||
|
umgebung["TP25_PINSPEC"] = spec
|
||||||
|
r = subprocess.run(["bash", PINM_SH, "set"], capture_output=True, text=True,
|
||||||
|
timeout=90, env=umgebung)
|
||||||
|
else:
|
||||||
|
r = subprocess.run(["bash", PINM_SH, "off"], capture_output=True, text=True,
|
||||||
|
timeout=90)
|
||||||
|
_pinm["msg"] = r.stdout.strip() or r.stderr.strip() or "ok"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
_pin["msg"] = "Fehler: %s" % e
|
_pinm["msg"] = "Fehler: %s" % e
|
||||||
finally:
|
finally:
|
||||||
_pin["busy"] = False
|
_pinm["busy"] = False
|
||||||
_pin["stand"] = 0.0
|
_pinm["stand"] = time.time()
|
||||||
pin_lesen()
|
|
||||||
_pin["busy"] = True
|
_pinm["busy"] = True
|
||||||
_pin["msg"] = "schaltet um …"
|
_pinm["msg"] = "setze Pin …" if spec else "löse Pin …"
|
||||||
|
_pinm["pins"] = pins
|
||||||
# Der Klick gehoert in dieselbe Zeitachse wie alles andere — sonst laesst sich hinterher
|
# Der Klick gehoert in dieselbe Zeitachse wie alles andere — sonst laesst sich hinterher
|
||||||
# nicht sagen, welche Messpunkte zu welchem AP gehoeren.
|
# nicht sagen, welche Messpunkte zu welchem Pin gehoeren.
|
||||||
write_marker("PIN=%s" % ziel)
|
write_marker("PINM=%s" % (spec or "off"))
|
||||||
|
threading.Thread(target=lauf, daemon=True).start()
|
||||||
|
|
||||||
|
|
||||||
|
def pinm_verify():
|
||||||
|
"""tp25-pin-multi.sh verify im Thread — zeigt Regeln + aktuelle Anmeldungen."""
|
||||||
|
def lauf():
|
||||||
|
try:
|
||||||
|
r = subprocess.run(["bash", PINM_SH, "verify"], capture_output=True, text=True,
|
||||||
|
timeout=60)
|
||||||
|
_pinm["msg"] = r.stdout.strip() or r.stderr.strip() or "ok"
|
||||||
|
except Exception as e:
|
||||||
|
_pinm["msg"] = "Fehler: %s" % e
|
||||||
|
finally:
|
||||||
|
_pinm["busy"] = False
|
||||||
|
_pinm["stand"] = time.time()
|
||||||
|
|
||||||
|
_pinm["busy"] = True
|
||||||
|
_pinm["msg"] = "prüfe …"
|
||||||
threading.Thread(target=lauf, daemon=True).start()
|
threading.Thread(target=lauf, daemon=True).start()
|
||||||
|
|
||||||
|
|
||||||
@@ -391,27 +418,42 @@ def fmt_bps(v):
|
|||||||
|
|
||||||
|
|
||||||
def render(st):
|
def render(st):
|
||||||
w = sampler_tail()
|
w = wan_tail()
|
||||||
rest = max(0, int(st.get("ends", 0) - time.time())) if st.get("ends") else 0
|
rest = max(0, int(st.get("ends", 0) - time.time())) if st.get("ends") else 0
|
||||||
mode = st.get("mode", "wartet")
|
mode = st.get("mode", "wartet")
|
||||||
|
|
||||||
banner = ""
|
banner = ""
|
||||||
alarm = False # nur DIESER Zustand loest Ton + Vibration aus, nicht jeder Banner
|
alarm = False # nur DIESER Zustand loest Ton + Vibration aus, nicht jeder Banner
|
||||||
# 🔴 Der wichtigste Alarm der Seite, seit es den AP-Pin gibt (2026-08-22): ein vom Pin
|
haengende = []
|
||||||
# ABGEWIESENER Verbindungsversuch ist in den Ping-Daten nicht von einem Funkloch zu
|
|
||||||
# unterscheiden -- und der Manager protokolliert die Abweisung auf info-Ebene nicht.
|
# 🔴 Der wichtigste Alarm der Seite, seit es den Pin gibt (2026-08-22, generalisiert
|
||||||
# Android quittiert sie mit "Verbindung fehlgeschlagen" und WAEHLT DAS NETZ AB, danach
|
# 2026-09-04): ein vom Pin ABGEWIESENER Verbindungsversuch ist in den Ping-Daten nicht
|
||||||
# misst der restliche Rundgang nur noch Stille. Deshalb wird "haengt nirgends" hier
|
# von einem Funkloch zu unterscheiden -- und der Manager protokolliert die Abweisung auf
|
||||||
# nicht klein in der WLAN-Zeile gezeigt, sondern schreit.
|
# info-Ebene nicht. Android quittiert sie mit "Verbindung fehlgeschlagen" und WAEHLT DAS
|
||||||
if pin_lesen() in ("ap1", "ap2") and w.get("iface") == "absent" and w.get("age", 999) < 30:
|
# NETZ AB, danach misst der restliche Rundgang nur noch Stille. Deshalb wird "haengt
|
||||||
alarm = True
|
# nirgends" hier nicht klein in der Client-Zeile gezeigt, sondern schreit.
|
||||||
banner = ('<div class="banner warn">📵 S22 HÄNGT NIRGENDS — Display an und wieder auf '
|
clients = []
|
||||||
'dirtyvader-main verbinden. Der Pin wirft es vom gesperrten AP; Android '
|
for name, ip, mac in geraete():
|
||||||
'wählt das Netz dann von selbst ab. <b>Nicht weitergehen</b>, sonst misst '
|
t = client_tail(name)
|
||||||
'du Stille statt Abdeckung.</div>')
|
pl = ping_loss(name)
|
||||||
|
iface = t.get("iface", "")
|
||||||
|
ap = ap_map().get(iface, iface) if iface else ""
|
||||||
|
signal = t.get("signal", "")
|
||||||
|
frisch = t.get("age", 999) < 30
|
||||||
|
gepinnt = name in _pinm["pins"]
|
||||||
|
if gepinnt and iface == "absent" and frisch:
|
||||||
|
alarm = True
|
||||||
|
haengende.append(name)
|
||||||
|
clients.append((name, ip, mac, iface, ap, signal, pl, frisch, gepinnt))
|
||||||
|
|
||||||
|
if haengende:
|
||||||
|
banner = ('<div class="banner warn">📵 <b>%s</b> HÄNGT NIRGENDS — der Pin wirft das '
|
||||||
|
'Geraet vom gesperrten AP; Android waehlt das Netz dann von selbst ab. '
|
||||||
|
'<b>Nicht weitergehen</b>, sonst misst du Stille statt Abdeckung.</div>'
|
||||||
|
% ", ".join(haengende))
|
||||||
elif not st.get("driver"):
|
elif not st.get("driver"):
|
||||||
banner = ('<div class="banner done">ℹ️ Keine Ping-Aufzeichnung — Räume markieren und '
|
banner = ('<div class="banner done">ℹ️ Keine Ping-Aufzeichnung — Räume markieren und '
|
||||||
'AP-Pin gehen trotzdem. Für die Messkurve <b>tp25-walk-messung.sh</b> starten.</div>')
|
'pinnen geht trotzdem. Für die Messkurve <b>tp25-walk-messung.sh</b> starten.</div>')
|
||||||
elif mode == "langzeit":
|
elif mode == "langzeit":
|
||||||
banner = ('<div class="banner done">✅ MESSUNG ABGESCHLOSSEN — die Zahlen liegen vor. '
|
banner = ('<div class="banner done">✅ MESSUNG ABGESCHLOSSEN — die Zahlen liegen vor. '
|
||||||
'Ab jetzt läuft nur noch die Langzeit-Aufzeichnung.</div>')
|
'Ab jetzt läuft nur noch die Langzeit-Aufzeichnung.</div>')
|
||||||
@@ -423,86 +465,65 @@ def render(st):
|
|||||||
countdown = ('<div class="cd" id="cd" data-ends="%d">noch ~%d s</div>' % (st.get("ends", 0), rest)
|
countdown = ('<div class="cd" id="cd" data-ends="%d">noch ~%d s</div>' % (st.get("ends", 0), rest)
|
||||||
if rest else '<div class="cd" id="cd" data-ends="0"></div>')
|
if rest else '<div class="cd" id="cd" data-ends="0"></div>')
|
||||||
|
|
||||||
wlan = "—"
|
# Client-Karten: je Geraet Name, AP+Signal, Ping-Verlust der letzten ~30 s.
|
||||||
if w.get("iface"):
|
karten = ""
|
||||||
wlan = "%s · %s dBm · tx %s / rx %s" % (
|
for name, ip, mac, iface, ap, signal, pl, frisch, gepinnt in clients:
|
||||||
ap_map().get(w["iface"], w["iface"]), w.get("signal", "?"),
|
if iface and iface != "absent":
|
||||||
fmt_bps(w.get("sta_tx_bps")), fmt_bps(w.get("sta_rx_bps")))
|
status = ("%s · %s dBm" % (ap, signal)) if signal else ap
|
||||||
|
elif iface == "absent":
|
||||||
|
status = "<b>nirgends</b>"
|
||||||
|
else:
|
||||||
|
status = "—"
|
||||||
|
if pl:
|
||||||
|
verloren, gesamt = pl
|
||||||
|
ping = "%d/%d verloren" % (verloren, gesamt)
|
||||||
|
else:
|
||||||
|
ping = "—"
|
||||||
|
karten += ('<div class="card"><div class="phase">%s</div>'
|
||||||
|
'<div class="inst"><b>%s</b></div>'
|
||||||
|
'<div class="cval">%s</div>'
|
||||||
|
'<div class="cval">Ping: %s</div></div>'
|
||||||
|
% ("📌 gepinnt" if gepinnt else "Client", name, status, ping))
|
||||||
|
|
||||||
ziel = pin_lesen()
|
# Pin-UI: je Client ein Ziel-Dropdown, dazu setzen/loesen/verify.
|
||||||
ip_ap, s22_ap = _ap_von(_live["iphone"]), _ap_von(_live["s22"])
|
ziele = pin_targets()
|
||||||
frisch = time.time() - _live["stand"] < 15
|
|
||||||
|
|
||||||
# Drei Zustaende, nicht zwei. "S22 kommt nicht rein" ist ein ERGEBNIS (der Gegen-AP
|
def optionen(aktuell):
|
||||||
# traegt hier nicht), kein Defekt -- es darf deshalb nicht wie ein Fehler aussehen und
|
out = ['<option value="frei"%s>frei</option>' % (" selected" if aktuell == "frei" else "")]
|
||||||
# nicht ewig Alarm schlagen.
|
for ap, band in ziele:
|
||||||
# 🔴 "beide angemeldet" ist NICHT das Kriterium -- der Messpunkt taugt nur, wenn die
|
wert = "%s|%s" % (ap, band)
|
||||||
# beiden Telefone auf VERSCHIEDENEN APs sitzen. Am 2026-08-22 meldete die Seite
|
label = "%s / %s GHz" % (ap, "5" if band == "5" else "2,4")
|
||||||
# "✅ beide angemeldet, auf verschiedenen APs", waehrend beide auf ap2 hingen: ein
|
out.append('<option value="%s"%s>%s</option>'
|
||||||
# gruenes Licht fuer eine Messung, die es gar nicht gab. Wieder derselbe Fehler --
|
% (wert, " selected" if wert == aktuell else "", label))
|
||||||
# die Anzeige geprueft statt der Wirkung.
|
return "".join(out)
|
||||||
getrennt = bool(ip_ap and s22_ap and ip_ap != s22_ap)
|
|
||||||
bereit = bool(frisch and getrennt)
|
|
||||||
gleich = bool(frisch and ip_ap and s22_ap and ip_ap == s22_ap)
|
|
||||||
wartet = bool(frisch and ip_ap and not s22_ap and not _auto["aufgegeben"])
|
|
||||||
if wartet and _auto["seit_weg"] and time.time() - _auto["seit_weg"] > 45:
|
|
||||||
_auto["aufgegeben"] = ziel or "?"
|
|
||||||
write_marker("UNERREICHBAR=%s" % (ziel or "?"))
|
|
||||||
wartet = False
|
|
||||||
|
|
||||||
def z(ap, sig, mac_name):
|
zeilen = ""
|
||||||
if not frisch:
|
for name, ip, mac in geraete():
|
||||||
return "%s: —" % mac_name
|
zeilen += ('<div class="pinzeile"><label>%s</label>'
|
||||||
return ("%s: <b>%s</b> %s dBm" % (mac_name, ap, sig)) if ap else ("%s: <b>nirgends</b>" % mac_name)
|
'<select name="pin_%s">%s</select></div>'
|
||||||
|
% (name, name, optionen(_pinm["pins"].get(name, "frei"))))
|
||||||
|
|
||||||
doppel = "%s · %s" % (z(ip_ap, _live["iphone_sig"], "iPhone"),
|
if _pinm["busy"]:
|
||||||
z(s22_ap, _live["s22_sig"], "S22"))
|
pinstatus = "⏳ %s" % _pinm["msg"].replace("\n", "<br>")
|
||||||
|
elif _pinm["msg"]:
|
||||||
if _pin["busy"]:
|
pinstatus = _pinm["msg"].replace("\n", "<br>")
|
||||||
pinstatus = "⏳ %s<br>%s" % (_pin["msg"], doppel)
|
elif not ziele:
|
||||||
elif _auto["aufgegeben"]:
|
pinstatus = "Manager nicht erreichbar — keine Pin-Ziele."
|
||||||
pinstatus = ("🚫 S22 bekommt <b>%s</b> hier nicht — <b>das ist das Messergebnis</b>, "
|
|
||||||
"Raum markieren und weitergehen.<br>%s" % (_auto["aufgegeben"], doppel))
|
|
||||||
elif bereit:
|
|
||||||
pinstatus = "✅ beide angemeldet, <b>verschiedene APs</b> — Messpunkt zählt<br>%s" % doppel
|
|
||||||
elif gleich and _auto["an"]:
|
|
||||||
pinstatus = ("⏳ beide auf <b>%s</b> — Gegen-AP wird gerade nachgezogen, "
|
|
||||||
"noch nicht weitergehen<br>%s" % (ip_ap, doppel))
|
|
||||||
elif gleich:
|
|
||||||
pinstatus = ("⚠️ beide auf <b>%s</b> — <b>kein Gegen-AP-Messpunkt.</b> "
|
|
||||||
"Automatik einschalten oder S22 von Hand pinnen.<br>%s" % (ip_ap, doppel))
|
|
||||||
elif wartet:
|
|
||||||
pinstatus = "⏳ S22 sucht <b>%s</b> …<br>%s" % (ziel or "?", doppel)
|
|
||||||
elif ziel in ("ap1", "ap2"):
|
|
||||||
pinstatus = "S22 gepinnt auf <b>%s</b><br>%s" % (ziel, doppel)
|
|
||||||
elif ziel == "off":
|
|
||||||
pinstatus = "kein Pin — S22 roamt frei<br>%s" % doppel
|
|
||||||
else:
|
else:
|
||||||
pinstatus = "Zustand unbekannt (Manager nicht erreichbar?)"
|
pinstatus = "Je Client AP/Band wählen, dann „Pin setzen“."
|
||||||
pinkarte = (
|
|
||||||
'<div class="card"><span id="sig" data-alarm="%d" data-busy="%d" data-pin="%s" '
|
pinm_karte = (
|
||||||
'data-bereit="%d"></span>'
|
'<div class="card"><span id="sig" data-alarm="%d" data-busy="%d"></span>'
|
||||||
'<div class="phase">AP-Pin S22</div>'
|
'<div class="phase">Pinnen (Multi-Pin)</div>'
|
||||||
'<div class="inst" id="pininst">%s</div><div class="raster">'
|
'<div class="inst" id="pininst">%s</div>'
|
||||||
'<form method="post" action="/pin?ap=ap1" onsubmit="return sendpin(event,\'ap1\')">'
|
'<form method="post" action="/pinmulti" onsubmit="return sendpinm(event)">'
|
||||||
'<button class="raum%s" type="submit">ap1</button></form>'
|
'%s'
|
||||||
'<form method="post" action="/pin?ap=ap2" onsubmit="return sendpin(event,\'ap2\')">'
|
'<button class="ok" type="submit">📌 Pin setzen</button></form>'
|
||||||
'<button class="raum%s" type="submit">ap2</button></form>'
|
'<form method="post" action="/pinmulti?off=1" onsubmit="return sendpinmoff(event)">'
|
||||||
'</div>'
|
'<button class="cam" type="submit">Pin lösen</button></form>'
|
||||||
'<form method="post" action="/auto" onsubmit="return sendauto(event)">'
|
'<form method="post" action="/pinmulti?verify=1" onsubmit="return sendpinmoff(event)">'
|
||||||
'<button class="%s" style="margin-top:8px" type="submit">%s</button></form>'
|
'<button class="cam" type="submit">verify</button></form>'
|
||||||
'<form method="post" action="/pin?ap=off" onsubmit="return sendpin(event,\'off\')">'
|
'</div>' % (1 if alarm else 0, 1 if _pinm["busy"] else 0, pinstatus, zeilen))
|
||||||
'<button class="cam" style="margin-top:8px" type="submit">Pin lösen</button></form>'
|
|
||||||
'</div>' % (1 if alarm else 0, 1 if _pin["busy"] else 0, ziel or "?",
|
|
||||||
1 if bereit else 0,
|
|
||||||
pinstatus,
|
|
||||||
" hier" if ziel == "ap1" else "",
|
|
||||||
" hier" if ziel == "ap2" else "",
|
|
||||||
"ok" if _auto["an"] else "cam",
|
|
||||||
"🔁 Gegen-AP: AN (folgt dem iPhone)" if _auto["an"]
|
|
||||||
else "🔁 Gegen-AP automatisch",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
raum, seit = current_room()
|
raum, seit = current_room()
|
||||||
# 🔴 Die Kacheln hingen bis 2026-08-22 am Treiber-Zustand -- ohne laufenden TP6-Treiber
|
# 🔴 Die Kacheln hingen bis 2026-08-22 am Treiber-Zustand -- ohne laufenden TP6-Treiber
|
||||||
@@ -547,8 +568,9 @@ def render(st):
|
|||||||
return PAGE.format(
|
return PAGE.format(
|
||||||
banner=banner, title=st.get("title", "—"), phase=st.get("phase", "—"),
|
banner=banner, title=st.get("title", "—"), phase=st.get("phase", "—"),
|
||||||
instruction=st.get("instruction", ""), prog=prog, countdown=countdown,
|
instruction=st.get("instruction", ""), prog=prog, countdown=countdown,
|
||||||
raumzeile=raumzeile, pinkarte=pinkarte, wlan=wlan, wan="rx %s / tx %s · CPU %s%%" % (fmt_bps(w.get("wan_rx")),
|
raumzeile=raumzeile, clients=karten, pinm_karte=pinm_karte,
|
||||||
fmt_bps(w.get("wan_tx")), w.get("cpu", "?")),
|
wan="rx %s / tx %s · CPU %s%%" % (fmt_bps(w.get("wan_rx")),
|
||||||
|
fmt_bps(w.get("wan_tx")), w.get("cpu", "?")),
|
||||||
last=last_marker(), buttons=btns, notaus=notaus,
|
last=last_marker(), buttons=btns, notaus=notaus,
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -579,6 +601,11 @@ button:active{{filter:brightness(1.3)}}
|
|||||||
.raster form{{margin:0}}
|
.raster form{{margin:0}}
|
||||||
.raum{{margin:0;padding:16px 8px;font-size:16px;background:#1e3a5f}}
|
.raum{{margin:0;padding:16px 8px;font-size:16px;background:#1e3a5f}}
|
||||||
.raum.hier{{background:#0ea5e9;color:#06202e;box-shadow:0 0 0 3px #7dd3fc inset}}
|
.raum.hier{{background:#0ea5e9;color:#06202e;box-shadow:0 0 0 3px #7dd3fc inset}}
|
||||||
|
.cval{{font:14px/1.6 ui-monospace,Menlo,monospace;color:#9aa4b2;margin-top:2px}}
|
||||||
|
.pinzeile{{display:flex;align-items:center;gap:10px;margin:8px 0}}
|
||||||
|
.pinzeile label{{flex:0 0 90px;font-weight:600}}
|
||||||
|
.pinzeile select{{flex:1;padding:10px;font-size:16px;background:#262b34;color:#e8eaed;
|
||||||
|
border:1px solid #2a2f3a;border-radius:8px}}
|
||||||
form{{margin:0}}
|
form{{margin:0}}
|
||||||
</style>
|
</style>
|
||||||
{banner}
|
{banner}
|
||||||
@@ -589,10 +616,10 @@ form{{margin:0}}
|
|||||||
{countdown}
|
{countdown}
|
||||||
{prog}
|
{prog}
|
||||||
</div>
|
</div>
|
||||||
{pinkarte}
|
{clients}
|
||||||
|
{pinm_karte}
|
||||||
{raumzeile}
|
{raumzeile}
|
||||||
<div class="live">WLAN {wlan}
|
<div class="live">WAN {wan}
|
||||||
WAN {wan}
|
|
||||||
Marker {last}</div>
|
Marker {last}</div>
|
||||||
{buttons}
|
{buttons}
|
||||||
{notaus}
|
{notaus}
|
||||||
@@ -638,18 +665,10 @@ function alarmton(){{ ton(880,180,0); ton(660,180,0.24); ton(880,260,0.48);
|
|||||||
function okton(){{ ton(1320,90,0); ton(1760,120,0.11);
|
function okton(){{ ton(1320,90,0); ton(1760,120,0.11);
|
||||||
try{{ navigator.vibrate && navigator.vibrate(90); }}catch(e){{}} }}
|
try{{ navigator.vibrate && navigator.vibrate(90); }}catch(e){{}} }}
|
||||||
|
|
||||||
function bereitton(){{ ton(1180,110,0); ton(1580,150,0.13);
|
var letzterAlarm = 0, letzterBusy = 0, alarmTimer = null;
|
||||||
try{{ navigator.vibrate && navigator.vibrate([60,60,60]); }}catch(e){{}} }}
|
|
||||||
|
|
||||||
var letzterAlarm = 0, letzterBusy = 0, letzterBereit = 0, alarmTimer = null;
|
|
||||||
function signale(){{
|
function signale(){{
|
||||||
var sig = document.getElementById('sig'); if(!sig) return;
|
var sig = document.getElementById('sig'); if(!sig) return;
|
||||||
var alarm = sig.dataset.alarm === '1', busy = sig.dataset.busy === '1';
|
var alarm = sig.dataset.alarm === '1', busy = sig.dataset.busy === '1';
|
||||||
var bereit = sig.dataset.bereit === '1';
|
|
||||||
// Der Piep, auf den Christoph wartet: beide Telefone haengen, auf verschiedenen APs --
|
|
||||||
// der Messpunkt ist vollstaendig, weitergehen. Nur bei ZUSTANDSWECHSEL, nicht dauernd.
|
|
||||||
if(bereit && !letzterBereit) bereitton();
|
|
||||||
letzterBereit = bereit ? 1 : 0;
|
|
||||||
if(alarm && !letzterAlarm){{
|
if(alarm && !letzterAlarm){{
|
||||||
alarmton();
|
alarmton();
|
||||||
// Solange nichts geschieht, alle 12 s erinnern -- ein einmaliger Piep in der Hosentasche
|
// Solange nichts geschieht, alle 12 s erinnern -- ein einmaliger Piep in der Hosentasche
|
||||||
@@ -657,7 +676,7 @@ function signale(){{
|
|||||||
if(!alarmTimer) alarmTimer = setInterval(alarmton, 12000);
|
if(!alarmTimer) alarmTimer = setInterval(alarmton, 12000);
|
||||||
}}
|
}}
|
||||||
if(!alarm && alarmTimer){{ clearInterval(alarmTimer); alarmTimer = null; }}
|
if(!alarm && alarmTimer){{ clearInterval(alarmTimer); alarmTimer = null; }}
|
||||||
if(!busy && letzterBusy) okton(); // Umschalten fertig
|
if(!busy && letzterBusy) okton(); // Pin-Aktion fertig
|
||||||
letzterAlarm = alarm ? 1 : 0; letzterBusy = busy ? 1 : 0;
|
letzterAlarm = alarm ? 1 : 0; letzterBusy = busy ? 1 : 0;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
@@ -672,28 +691,22 @@ function sendstop(ev){{
|
|||||||
return false;
|
return false;
|
||||||
}}catch(e){{ return true; }}
|
}}catch(e){{ return true; }}
|
||||||
}}
|
}}
|
||||||
function sendauto(ev){{
|
function sendpinm(ev){{
|
||||||
try{{
|
try{{
|
||||||
ev.preventDefault(); unlock(); okton();
|
ev.preventDefault(); unlock(); okton();
|
||||||
fetch('/auto', {{method:'POST'}}).catch(function(){{}});
|
var inst = document.getElementById('pininst');
|
||||||
setTimeout(refresh, 400); setTimeout(refresh, 3000);
|
if(inst) inst.innerHTML = '⏳ setze Pin …<br>Gepinnte Geräte fliegen kurz aus dem WLAN und verbinden sich neu.';
|
||||||
|
var fd = new FormData(ev.target);
|
||||||
|
fetch('/pinmulti', {{method:'POST', body: new URLSearchParams(fd)}}).catch(function(){{}});
|
||||||
|
setTimeout(refresh, 3000); setTimeout(refresh, 9000); setTimeout(refresh, 20000);
|
||||||
return false;
|
return false;
|
||||||
}}catch(e){{ return true; }}
|
}}catch(e){{ return true; }}
|
||||||
}}
|
}}
|
||||||
function sendpin(ev, ap){{
|
function sendpinmoff(ev){{
|
||||||
try{{
|
try{{
|
||||||
ev.preventDefault(); unlock();
|
ev.preventDefault(); unlock(); okton();
|
||||||
// 🔴 Der Klick kommt vom S22 SELBST -- also von genau dem Geraet, das der Pin gleich vom
|
fetch(ev.target.action, {{method:'POST'}}).catch(function(){{}});
|
||||||
// WLAN wirft. Die Antwort auf dieses fetch() kommt deshalb womoeglich nie an, und die
|
setTimeout(refresh, 3000); setTimeout(refresh, 9000);
|
||||||
// Seite kann sich nicht mehr nachladen. Die Anweisung muss JETZT auf dem Schirm
|
|
||||||
// stehen, solange die Verbindung noch steht.
|
|
||||||
var inst = document.getElementById('pininst');
|
|
||||||
if(inst) inst.innerHTML = '⏳ schalte auf <b>' + ap + '</b> …<br>' +
|
|
||||||
'<b>Dieses Telefon fliegt jetzt aus dem WLAN.</b> Wenn die Seite haengt: in den ' +
|
|
||||||
'WLAN-Einstellungen wieder auf <b>dirtyvader-main</b> verbinden, dann laedt sie von selbst.';
|
|
||||||
okton();
|
|
||||||
fetch('/pin?ap=' + encodeURIComponent(ap), {{method:'POST'}}).catch(function(){{}});
|
|
||||||
setTimeout(refresh, 3000); setTimeout(refresh, 9000); setTimeout(refresh, 20000);
|
|
||||||
return false;
|
return false;
|
||||||
}}catch(e){{ return true; }}
|
}}catch(e){{ return true; }}
|
||||||
}}
|
}}
|
||||||
@@ -767,37 +780,27 @@ class Handler(BaseHTTPRequestHandler):
|
|||||||
self._send(200, json.dumps({"ok": True})) if "text/html" not in (
|
self._send(200, json.dumps({"ok": True})) if "text/html" not in (
|
||||||
self.headers.get("Accept") or "") else self._back()
|
self.headers.get("Accept") or "") else self._back()
|
||||||
return
|
return
|
||||||
if self.path.startswith("/auto"):
|
if self.path.startswith("/pinmulti"):
|
||||||
# Ohne Parameter umschalten (das macht der Knopf), mit ?an=0|1 gezielt setzen --
|
if _pinm["busy"]:
|
||||||
# tp25-runde.sh schaltet damit beim Start scharf. Ein Toggle waere dort falsch:
|
if "text/html" in (self.headers.get("Accept") or ""):
|
||||||
# laeuft die Konsole schon mit Automatik, haette der Start sie AUSgeschaltet.
|
self._back()
|
||||||
if "an=" in self.path:
|
else:
|
||||||
_auto["an"] = self.path.split("an=", 1)[1].split("&")[0] in ("1", "true", "an")
|
self._send(409, json.dumps({"error": "laeuft schon"}))
|
||||||
|
return
|
||||||
|
qs = urllib.parse.parse_qs(urllib.parse.urlparse(self.path).query)
|
||||||
|
if qs.get("off"):
|
||||||
|
pinm_aktion("")
|
||||||
|
elif qs.get("verify"):
|
||||||
|
pinm_verify()
|
||||||
else:
|
else:
|
||||||
_auto["an"] = not _auto["an"]
|
laenge = int(self.headers.get("Content-Length") or 0)
|
||||||
_auto["aufgegeben"] = ""
|
body = self.rfile.read(laenge).decode("utf-8") if laenge else ""
|
||||||
_auto["seit_weg"] = time.time()
|
form = urllib.parse.parse_qs(body)
|
||||||
write_marker("AUTO=%s" % ("an" if _auto["an"] else "aus"))
|
pinm_aktion(build_spec(form))
|
||||||
if _auto["an"]:
|
|
||||||
entscheide()
|
|
||||||
if "text/html" in (self.headers.get("Accept") or ""):
|
if "text/html" in (self.headers.get("Accept") or ""):
|
||||||
self._back()
|
self._back()
|
||||||
else:
|
else:
|
||||||
self._send(200, json.dumps({"ok": True, "auto": _auto["an"]}))
|
self._send(200, json.dumps({"ok": True}))
|
||||||
return
|
|
||||||
if self.path.startswith("/pin?"):
|
|
||||||
ap = urllib.parse.unquote(self.path.split("ap=", 1)[1].split("&")[0])
|
|
||||||
if ap not in ("ap1", "ap2", "off"):
|
|
||||||
self._send(400, json.dumps({"error": "ap muss ap1, ap2 oder off sein"}))
|
|
||||||
return
|
|
||||||
if _pin["busy"]:
|
|
||||||
self._send(409, json.dumps({"error": "laeuft schon"}))
|
|
||||||
return
|
|
||||||
pin_setzen(ap)
|
|
||||||
if "text/html" in (self.headers.get("Accept") or ""):
|
|
||||||
self._back()
|
|
||||||
else:
|
|
||||||
self._send(200, json.dumps({"ok": True, "ziel": ap}))
|
|
||||||
return
|
return
|
||||||
if self.path.startswith("/m?"):
|
if self.path.startswith("/m?"):
|
||||||
# 🔴 Erst dekodieren, dann NICHT hochschreiben: "RAUM%3DDiele%20OG" wurde sonst zu
|
# 🔴 Erst dekodieren, dann NICHT hochschreiben: "RAUM%3DDiele%20OG" wurde sonst zu
|
||||||
@@ -836,8 +839,7 @@ if __name__ == "__main__":
|
|||||||
print("Marker -> %s" % markers_pfad())
|
print("Marker -> %s" % markers_pfad())
|
||||||
for iface, wo in sorted(ap_map().items()):
|
for iface, wo in sorted(ap_map().items()):
|
||||||
print(" %-14s %s" % (iface, wo))
|
print(" %-14s %s" % (iface, wo))
|
||||||
threading.Thread(target=beobachter, daemon=True).start()
|
# 🔴 Threading ist Pflicht, seit der Pin dazugekommen ist: das Setzen redet ueber SSH
|
||||||
# 🔴 Threading ist Pflicht, seit der Pin dazugekommen ist: das Umschalten redet ueber SSH
|
|
||||||
# mit dem Manager und braucht ~8 s. Mit dem alten HTTPServer (single-threaded) haette
|
# mit dem Manager und braucht ~8 s. Mit dem alten HTTPServer (single-threaded) haette
|
||||||
# das die ganze Konsole blockiert -- inklusive Not-Aus.
|
# das die ganze Konsole blockiert -- inklusive Not-Aus.
|
||||||
ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
|
ThreadingHTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
/interface bridge vlan set [find where vlan-ids=10 and !dynamic] tagged=ether1,ether3,ether8,ether13,ether15
|
||||||
|
/interface bridge vlan set [find where vlan-ids=20 and !dynamic] tagged=ether3,ether8,ether13,ether15
|
||||||
|
/interface bridge vlan set [find where vlan-ids=30 and !dynamic] tagged=ether1,ether3,ether8,ether15
|
||||||
|
/interface bridge vlan set [find where vlan-ids=40 and !dynamic] tagged=ether1,ether3,ether8,ether13,ether15 untagged=ether2,ether4,ether5
|
||||||
|
/interface bridge vlan set [find where vlan-ids=50 and !dynamic] tagged=ether1,ether3,ether8,ether15
|
||||||
|
/interface bridge vlan set [find where vlan-ids=90 and !dynamic] tagged=ether1,ether3,ether8,ether15
|
||||||
|
/interface bridge vlan set [find where vlan-ids=99 and !dynamic] tagged=ether3,ether8,ether15,bridge
|
||||||
15
docs/transaktionen/TEST-ether3-trunk-ap1-sideboard-2.json
Normal file
15
docs/transaktionen/TEST-ether3-trunk-ap1-sideboard-2.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"change_id": "TEST-ether3-trunk-ap1-sideboard-2",
|
||||||
|
"device": "crs326-core",
|
||||||
|
"scope": "bridge-vlan",
|
||||||
|
"match_config": [
|
||||||
|
"/interface bridge vlan"
|
||||||
|
],
|
||||||
|
"reason": "ap1 testweise am Sideboard, Uplink auf ether3 (Messung TP25)",
|
||||||
|
"started": "2026-09-03",
|
||||||
|
"expires": "2026-09-10",
|
||||||
|
"state": "committed",
|
||||||
|
"snapshot": "docs/transaktionen/TEST-ether3-trunk-ap1-sideboard-2.snapshot.rsc",
|
||||||
|
"device_backup": "",
|
||||||
|
"closed": "2026-09-03"
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Snapshot des Scopes 'bridge-vlan' auf crs326-core, 2026-09-03 (Transaktion TEST-ether3-trunk-ap1-sideboard-2)
|
||||||
|
# Bewusst NUR dieser Abschnitt — der Vollexport gehoert nicht ins Repo.
|
||||||
|
|
||||||
|
/interface bridge vlan
|
||||||
|
add bridge=bridge untagged=ether1,ether2 vlan-ids=1
|
||||||
|
add bridge=bridge tagged=ether1,ether8,ether13,ether15,ether21 vlan-ids=10
|
||||||
|
add bridge=bridge tagged=ether8,ether13,ether15,ether21 vlan-ids=20
|
||||||
|
add bridge=bridge tagged=ether1,ether8,ether15,ether21 untagged=ether14 \
|
||||||
|
vlan-ids=30
|
||||||
|
add bridge=bridge tagged=ether1,ether8,ether13,ether15,ether21 untagged=\
|
||||||
|
ether2,ether3,ether4,ether5 vlan-ids=40
|
||||||
|
add bridge=bridge tagged=ether1,ether8,ether15,ether21 vlan-ids=50
|
||||||
|
add bridge=bridge tagged=ether1,ether8,ether15,ether21 vlan-ids=90
|
||||||
|
add bridge=bridge tagged=ether8,ether15,ether21,bridge vlan-ids=99
|
||||||
|
add bridge=bridge tagged=ether1 untagged=ether16 vlan-ids=100
|
||||||
8
docs/transaktionen/TP25-ft-yes-secmain.commands.rsc
Normal file
8
docs/transaktionen/TP25-ft-yes-secmain.commands.rsc
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# TP25 — sec-main zurueck auf ft=yes / ft-over-ds=yes (Ruecknahme der 09-04-Entscheidung "ft=no als Dauerzustand")
|
||||||
|
# Go: Christoph 2026-09-14 ("go fuer ft=yes, dry-run vorlegen"), Terrasse-In-situ-Session
|
||||||
|
# Anlass: 2 Call-Aussetzer + ~30 Roams in 25 min — mit ft=no ist jeder Roam (Band- und AP-Wechsel)
|
||||||
|
# eine Voll-Reassoziation in Sekundenhoehe; genau darin sterben Anrufe.
|
||||||
|
# Nachweis, dass ft-over-ds=yes + 7.24.1 ruhig ist: Fund-E-Abschlussmessung 09-02 (4 reauth/8,6 h, 0 Ausfaelle).
|
||||||
|
# Nur am Manager noetig: sec-main wird per CAPsMAN an alle CAPs verteilt (Uniformitaet per Konstruktion);
|
||||||
|
# Inline-Overrides je CAP gibt es seit dem ap3-Fix 09-04 nicht mehr — Abnahme prueft sie trotzdem (Merkregel).
|
||||||
|
/interface wifi security set sec-main ft=yes ft-over-ds=yes
|
||||||
19
docs/transaktionen/TP25-kanalplan-ap1-live.commands.rsc
Normal file
19
docs/transaktionen/TP25-kanalplan-ap1-live.commands.rsc
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Live gesetzt am crs326-core 2026-09-03 (via MikroMCP run_command).
|
||||||
|
# Zwei Scopes betroffen; die Akte fuehrt formell nur wifi-channel — die zwei
|
||||||
|
# Provisioning-Adds sind hier mit-dokumentiert, weil das begin-Skript nur einen
|
||||||
|
# Scope je offener Akte kennt und sie inhaltlich zum selben Test gehoeren.
|
||||||
|
# Beim Commit gehoert BEIDES ins Template (nautobot/mikrotik_routeros.j2):
|
||||||
|
# - Zeilen 206-207: frequency=5180/5220 -> Frequenz-Liste 5260,5280 / 5300,5320
|
||||||
|
# - Zeilen 236f: neue Provisioning-Regeln fuer ap1 (radio-mac E5=5G, E6=2G)
|
||||||
|
|
||||||
|
# --- Scope wifi-channel ---
|
||||||
|
/interface wifi channel set [find name=ch-5g-ap1] frequency=5260,5280
|
||||||
|
/interface wifi channel set [find name=ch-5g-ap2] frequency=5300,5320
|
||||||
|
|
||||||
|
# --- Scope wifi-provisioning (mit-dokumentiert, nicht formell im scope der Akte) ---
|
||||||
|
/interface wifi provisioning add radio-mac=D0:EA:11:4D:40:E5 action=create-dynamic-enabled master-configuration=cfg-main-5g-ap1 slave-configurations=cfg-media,cfg-untrusted,cfg-guest comment="ap1 5GHz"
|
||||||
|
/interface wifi provisioning add radio-mac=D0:EA:11:4D:40:E6 action=create-dynamic-enabled master-configuration=cfg-main-2g-ap1 slave-configurations=cfg-iot,cfg-media,cfg-untrusted,cfg-guest comment="ap1 2.4GHz"
|
||||||
|
|
||||||
|
# Anschliessend am Manager:
|
||||||
|
/interface wifi capsman remote-cap provision [find where identity=ap1]
|
||||||
|
/interface wifi capsman remote-cap provision [find where identity=ap2]
|
||||||
15
docs/transaktionen/TP25-kanalplan-ap1-live.json
Normal file
15
docs/transaktionen/TP25-kanalplan-ap1-live.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"change_id": "TP25-kanalplan-ap1-live",
|
||||||
|
"device": "crs326-core",
|
||||||
|
"scope": "wifi-channel",
|
||||||
|
"match_config": [
|
||||||
|
"/interface wifi channel"
|
||||||
|
],
|
||||||
|
"reason": "TP25-Kanalplan live: ap1=5260/5280, ap2=5300/5320 (DFS Band B). Testet den Zielzustand nach TP25 (dritter AP + ap2-Umverteilung) am tatsaechlichen Aufstellort. Bei ok: Template auf Frequenz-Listen umstellen und committen. Bei nicht ok: zurueck auf 5180/5220 (Snapshot).",
|
||||||
|
"started": "2026-09-03",
|
||||||
|
"expires": "2026-09-10",
|
||||||
|
"state": "committed",
|
||||||
|
"snapshot": "docs/transaktionen/TP25-kanalplan-ap1-live.snapshot.rsc",
|
||||||
|
"device_backup": "",
|
||||||
|
"closed": "2026-09-04"
|
||||||
|
}
|
||||||
10
docs/transaktionen/TP25-kanalplan-ap1-live.snapshot.rsc
Normal file
10
docs/transaktionen/TP25-kanalplan-ap1-live.snapshot.rsc
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# Snapshot des Scopes 'wifi-channel' auf crs326-core, 2026-09-03 (Transaktion TP25-kanalplan-ap1-live)
|
||||||
|
# Bewusst NUR dieser Abschnitt — der Vollexport gehoert nicht ins Repo.
|
||||||
|
|
||||||
|
/interface wifi channel
|
||||||
|
add band=5ghz-ax frequency=5260,5280 name=ch-5g-ap1 width=20/40mhz
|
||||||
|
add band=5ghz-ax frequency=5300,5320 name=ch-5g-ap2 width=20/40mhz
|
||||||
|
add band=2ghz-ax frequency=2412 name=ch-2g-ap1 width=20mhz
|
||||||
|
add band=2ghz-ax frequency=2462 name=ch-2g-ap2 width=20mhz
|
||||||
|
add band=5ghz-ax frequency=5180 name=ch-5g-ap3 width=20/40mhz
|
||||||
|
add band=2ghz-ax frequency=2437 name=ch-2g-ap3 width=20mhz
|
||||||
10
docs/transaktionen/TP25-steering-5g-threshold.commands.rsc
Normal file
10
docs/transaktionen/TP25-steering-5g-threshold.commands.rsc
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# TP25 — 5G bricht frueher zur 2.4G ab (Terrasse sass am Rundgang 09-15 auf der -80/5G-Kante)
|
||||||
|
# Go: Christoph 2026-09-15 ("committen und threshold deployen")
|
||||||
|
# Nur 5G: neues Steering-Profil st-main-5g mit transition-threshold=-72 (statt Default -80) und
|
||||||
|
# transition-threshold-time=5s (statt 10s). 2G bleibt auf st-main (unset -80) — Reichweite draussen.
|
||||||
|
# Nur am Manager noetig: steering/config werden per CAPsMAN an die CAPs verteilt.
|
||||||
|
# Rollback = steering der drei Configs zurueck auf st-main + st-main-5g loeschen.
|
||||||
|
/interface wifi steering add name=st-main-5g neighbor-group=bf-main rrm=yes wnm=yes transition-threshold=-72 transition-threshold-time=5s
|
||||||
|
/interface wifi configuration set [find name=cfg-main-5g-ap1] steering=st-main-5g
|
||||||
|
/interface wifi configuration set [find name=cfg-main-5g-ap2] steering=st-main-5g
|
||||||
|
/interface wifi configuration set [find name=cfg-main-5g-ap3] steering=st-main-5g
|
||||||
15
docs/transaktionen/TP45-firewall-nautobot-154.commands.rsc
Normal file
15
docs/transaktionen/TP45-firewall-nautobot-154.commands.rsc
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# TP45 Schicht A — Nautobot-Literal in der Core-Firewall von .30 auf .154
|
||||||
|
#
|
||||||
|
# KEINE Transaktion: das ist eine dauerhafte Soll-Aenderung (Nautobot ist umgezogen,
|
||||||
|
# das Soll in Nautobot wurde mitgezogen). Die Datei liegt nur hier, weil
|
||||||
|
# gc-manual-plan.py eine Commands-Datei braucht.
|
||||||
|
#
|
||||||
|
# Kontext: die Regel *24 "TP5-E2: Nautobot->MGMT ssh" ist der LITERAL-Pfad und stand
|
||||||
|
# auf 10.40.20.30. Sie steht bei 0 Paketen — die Arbeit macht die Parallelregel *28
|
||||||
|
# "TP5-E2: Nautobot->MGMT ssh (list)" ueber src-address-list=nautobot (113 Pakete,
|
||||||
|
# Liste am 2026-09-21 um .154/.155/.156 erweitert). Der Literal ist damit
|
||||||
|
# funktional redundant, wird aber auf .154 gezogen, damit Soll (Nautobot-Daten
|
||||||
|
# `firewall_filter`) und Ist nicht auseinanderlaufen.
|
||||||
|
#
|
||||||
|
# Rollback: siehe TP45-firewall-nautobot-154.rollback.rsc
|
||||||
|
/ip firewall filter set [find comment="TP5-E2: Nautobot->MGMT ssh"] src-address=10.40.20.154
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Rueckbau TP45 Schicht A: Nautobot-Literal zurueck auf .30
|
||||||
|
# (nur noetig, wenn Nautobot wieder auf .30 zurueckzieht — die Adressliste ist die
|
||||||
|
# wirksame Ebene, dieser Literal ist der tote Parallelpfad.)
|
||||||
|
/ip firewall filter set [find comment="TP5-E2: Nautobot->MGMT ssh"] src-address=10.40.20.30
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user