Compare commits
73 Commits
c23628f62f
...
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 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,10 +1,6 @@
|
||||
.DS_Store
|
||||
.secrets
|
||||
|
||||
# Per-Projekt-MCP-Configs: können Tokens/URLs enthalten, projekt-spezifisch, nicht ins Repo
|
||||
.mcp.json
|
||||
|
||||
|
||||
# Python bytecode
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
77
CLAUDE.md
77
CLAUDE.md
@@ -6,7 +6,13 @@ DNS/DHCP-Ablösung, WLAN via **CAPsMAN auf dem Core** (TP28, 2026-08-17 — der
|
||||
|
||||
## 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.
|
||||
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)
|
||||
@@ -20,6 +26,12 @@ DNS/DHCP-Ablösung, WLAN via **CAPsMAN auf dem Core** (TP28, 2026-08-17 — der
|
||||
am 09-01 zurueckgebaut; Hebel 2–4 entfallen, **kein Geraete-Write** (Soll stand: `ft=yes ft-over-ds=yes`,
|
||||
**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)
|
||||
@@ -34,15 +46,30 @@ DNS/DHCP-Ablösung, WLAN via **CAPsMAN auf dem Core** (TP28, 2026-08-17 — der
|
||||
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.
|
||||
|
||||
## 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)
|
||||
|
||||
- **`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-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.
|
||||
- **`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.
|
||||
- **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.
|
||||
- **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`.
|
||||
|
||||
@@ -50,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:
|
||||
- Vorschlagen und mit dir abstimmen (nicht eigenmächtig anlegen).
|
||||
- Auf dein Okay hin anlegen: Quick-Index-Zeile + `### TP<n>`-Abschnitt mit Status `🔜`, Scope,
|
||||
Abhängigkeiten, Abgrenzung. Nächste freie Nummer vergeben (höchste vergebene + 1; Lücken aus
|
||||
verworfenen TPs bleiben frei).
|
||||
- Ist-Stand-Block mitziehen (Datum, betroffene Zeilen).
|
||||
- Auf dein Okay hin anlegen: **Gitea-Issue** `TPnn — <Kurztitel>` mit Labels `status:🔜`(todo) +
|
||||
passendem `thema:*` + Body (Anriss + Verweis auf den Prosa-Abschnitt) **und** einen
|
||||
`### TP<n>`-Prosa-Abschnitt in `docs/teilprojekte.md` (Scope, Abhängigkeiten, Abgrenzung).
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
@@ -80,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.) |
|
||||
| ~~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 |
|
||||
| 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² #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` |
|
||||
| **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` |
|
||||
| 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** — **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` · **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. |
|
||||
| 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`** |
|
||||
| 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-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` ✓ |
|
||||
@@ -126,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
|
||||
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`.
|
||||
- 🔴 **`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.
|
||||
|
||||
---
|
||||
@@ -258,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.
|
||||
- **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.
|
||||
- **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).
|
||||
- **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
|
||||
|
||||
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
|
||||
users:
|
||||
- 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
|
||||
block_auth_min: 15
|
||||
http_proxy: ""
|
||||
@@ -69,7 +71,7 @@ dns:
|
||||
ipset_file: ""
|
||||
bootstrap_prefer_ipv6: false
|
||||
upstream_timeout: 10s
|
||||
private_networks: []
|
||||
private_<redacted>: []
|
||||
use_private_ptr_resolvers: true
|
||||
local_ptr_upstreams: []
|
||||
use_dns64: false
|
||||
@@ -202,69 +204,9 @@ filtering:
|
||||
- domain: gitea.emsenhuber.net
|
||||
answer: 10.40.20.40
|
||||
enabled: true
|
||||
- domain: nautobot.emsenhuber.net
|
||||
answer: 10.40.20.40
|
||||
enabled: true
|
||||
- domain: ds-bf.emsenhuber.net
|
||||
answer: 10.40.20.40
|
||||
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
|
||||
answer: 10.40.20.10
|
||||
enabled: true
|
||||
@@ -316,7 +258,232 @@ clients:
|
||||
rdns: true
|
||||
dhcp: 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:
|
||||
enabled: true
|
||||
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()
|
||||
@@ -47,7 +47,7 @@ gitea.emsenhuber.net {
|
||||
# INTERN (Mesh/LAN; Split-Horizon zeigt diese Namen auf 10.40.20.40)
|
||||
# =========================================================================
|
||||
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
|
||||
|
||||
@@ -19,12 +19,35 @@ if [ -z "${TOKEN}" ]; then
|
||||
exit 1
|
||||
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"
|
||||
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})"
|
||||
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"
|
||||
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.
|
||||
@@ -2,15 +2,39 @@
|
||||
|
||||
> 🆕 **SSoT-Wechsel im Aufbau (TP44, seit 2026-09-02):** Die CF-Estate (Tunnel, WARP-Connector,
|
||||
> VPC-Services, Hyperdrive, Workers, Access-Apps) wird als **native `nautobot.cloud`-Objekte** in
|
||||
> Nautobot (`10.40.20.30`) geführt — Etappe 1 (manuelle Erstbefüllung) ✅. Seeder:
|
||||
> `nautobot/tp36-cf-estate-seed.py`, Detail: `docs/handoff-20260902-cf-estate-nautobot-ssot.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: → §10 (2026-07-10, TP16 UMGESETZT ✅).** §9 = Analyse/Zielbild, §10 = As-Built.
|
||||
> Abschnitte 1–8 sind Historie (Track A, 2026-06-30); die „3 Connectors gut/Zielbild"-Aussagen dort sind
|
||||
> durch §9/§10 **überholt** (Round-Robin = macvlan-Host-Isolation → 1/N-Ausfälle). Track A aus `docs/handoff-cf-tunnel-1033.md`.
|
||||
> **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`)
|
||||
| Tunnel | ID | Status | Conns | Verwendung |
|
||||
@@ -234,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 ✅
|
||||
> 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).
|
||||
|
||||
@@ -337,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)
|
||||
|
||||
> ✅ **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.
|
||||
|
||||
**Trennung der Concerns (wichtig):**
|
||||
@@ -452,6 +483,8 @@ Netzwerk-Namespace, sieht ohnehin alles), und die cloudflared-Connectoren laufen
|
||||
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.
|
||||
|
||||
</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
|
||||
|
||||
@@ -82,7 +82,7 @@ terpene.teilzeitladen.xyz {
|
||||
"type": "http",
|
||||
"url": "https://n8n-mcp.emsenhuber.net/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer 47a34f5589388d37d8b7503abac07b930df264c469ef0674aaeeea9b71dde847"
|
||||
"Authorization": "Bearer <redacted>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@@ -41,10 +41,10 @@ Manufacturer "Cloudflare"
|
||||
|
||||
## Werkzeug
|
||||
|
||||
`nautobot/tp36-cf-estate-seed.py` — **idempotenter** Seeder (get-or-create), Datenstand als Inline-Struktur.
|
||||
`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".
|
||||
*(Dateiname trägt noch `tp36`-Präfix aus dem ersten Arbeitsstand; inhaltlich TP44. Umbenennen bei Gelegenheit.)*
|
||||
*(Datei am 2026-09-06 von `tp36-…` auf `tp44-cf-estate-seed.py` umbenannt — Präfix jetzt korrekt.)*
|
||||
|
||||
## Gotchas (für Etappe 2 / Folgesessions)
|
||||
|
||||
@@ -54,15 +54,17 @@ Token aus `docs/nas-vlan20/stacks/210-nautobot.yml` (gitignored). Aufruf: Dry-Ru
|
||||
- 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`** eintragen (Quick-Index + Abschnitt + Ist-Stand), sobald die dortigen
|
||||
fremden Änderungen committet/geklärt sind.
|
||||
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-…` umbenennen.
|
||||
4. ✅ Seeder-Datei nach `tp44-cf-estate-seed.py` umbenannt — 2026-09-06.
|
||||
|
||||
## Verifikation (2026-09-02)
|
||||
|
||||
|
||||
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 |
|
||||
|
||||
| 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.
|
||||
|
||||
|
||||
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) |
|
||||
| [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 |
|
||||
| [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.
|
||||
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
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
### 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).
|
||||
|
||||
1162
docs/teilprojekte.md
1162
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`.
|
||||
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
|
||||
@@ -8,7 +8,8 @@
|
||||
"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": "open",
|
||||
"state": "committed",
|
||||
"snapshot": "docs/transaktionen/TP25-kanalplan-ap1-live.snapshot.rsc",
|
||||
"device_backup": ""
|
||||
"device_backup": "",
|
||||
"closed": "2026-09-04"
|
||||
}
|
||||
|
||||
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
|
||||
@@ -0,0 +1,3 @@
|
||||
# Ist-Stand der Regel VOR dem TP45-Schicht-A-Deploy (2026-09-21, gelesen per MikroMCP).
|
||||
/ip firewall filter
|
||||
add action=accept chain=forward comment="TP5-E2: Nautobot->MGMT ssh" dst-port=22 out-interface=vlan99 protocol=tcp src-address=10.40.20.30
|
||||
@@ -84,7 +84,7 @@ dns:
|
||||
ipset_file: ""
|
||||
bootstrap_prefer_ipv6: false
|
||||
upstream_timeout: 10s
|
||||
private_networks: []
|
||||
private_<redacted>: []
|
||||
use_private_ptr_resolvers: false
|
||||
local_ptr_upstreams: []
|
||||
use_dns64: false
|
||||
|
||||
14
jobs/__init__.py
Normal file
14
jobs/__init__.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""bf-infra Nautobot-Jobs (Git-Datenquelle).
|
||||
|
||||
Nautobot importiert nur dieses Paket; damit die enthaltenen Jobs sich
|
||||
registrieren, muss jedes Job-Modul hier importiert werden. Neue periodische
|
||||
externe->Nautobot-Syncs kommen als weiteres Modul dazu (kein per-Task-Container).
|
||||
"""
|
||||
from nautobot.apps.jobs import register_jobs
|
||||
|
||||
from .cf_estate_sync import CFEstateSync
|
||||
|
||||
# Nautobot importiert nur dieses Paket (bf_infra.jobs) und erwartet register_jobs()
|
||||
# HIER im __init__ (nicht im Submodul) — sonst: "No jobs were registered on loading
|
||||
# the bf_infra.jobs submodule". Neue Jobs: Klasse importieren + hier mitregistrieren.
|
||||
register_jobs(CFEstateSync)
|
||||
267
jobs/cf_estate_sync.py
Normal file
267
jobs/cf_estate_sync.py
Normal file
@@ -0,0 +1,267 @@
|
||||
"""
|
||||
TP44 Teil B — CF-Estate → Nautobot, als geplanter Nautobot-Job (ORM).
|
||||
|
||||
Kanonische Runtime des CF→Nautobot-Sync. Laeuft im nautobot-worker (kein extra
|
||||
Container), schreibt per ORM (keine Nautobot-Exposure), zieht die CF-API outbound.
|
||||
Portierung der Reconcile-Logik aus nautobot/tp44-cf-sync.py (REST) auf das ORM.
|
||||
|
||||
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/-Cron. Kuenftige Syncs = weitere Jobs hier.
|
||||
|
||||
Deckung: CloudNetwork (cfd_tunnel + warp_connector), CloudService (workers,
|
||||
access-apps, hyperdrive, VPC), Prefix-Assignments (teamnet/routes), Verwaisung
|
||||
(stale-Markierung statt Loeschen). ID-Matching + extra_config-Merge wie im Skript.
|
||||
|
||||
Token: Nautobot-Secret "cf-api-token" (Provider environment-variable -> CF_API_TOKEN
|
||||
am Worker). CF-Account-ID: Secret "cf-account-id" ODER Konstante unten.
|
||||
|
||||
CF bleibt operativer Master; Nautobot ist Dokumentations-SSoT (read-only Pull).
|
||||
"""
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import urllib.error
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from nautobot.apps.jobs import BooleanVar, Job
|
||||
from nautobot.cloud.models import (
|
||||
CloudAccount,
|
||||
CloudNetwork,
|
||||
CloudNetworkPrefixAssignment,
|
||||
CloudResourceType,
|
||||
CloudService,
|
||||
)
|
||||
from nautobot.dcim.models import Manufacturer
|
||||
from nautobot.extras.models import Secret
|
||||
from nautobot.ipam.models import Prefix
|
||||
|
||||
CF_API = "https://api.cloudflare.com/client/v4"
|
||||
CF_ACCOUNT_ID = "393c051f3accf51881d6c98fc5b66c36"
|
||||
|
||||
RESOURCE_TYPES = [
|
||||
("Cloudflare cloudflared Tunnel", "cloud", "cloudnetwork"),
|
||||
("Cloudflare WARP Connector", "cloud", "cloudnetwork"),
|
||||
("Cloudflare VPC Service", "cloud", "cloudservice"),
|
||||
("Cloudflare Hyperdrive", "cloud", "cloudservice"),
|
||||
("Cloudflare Worker", "cloud", "cloudservice"),
|
||||
("Cloudflare Access Application", "cloud", "cloudservice"),
|
||||
]
|
||||
|
||||
|
||||
class CFEstateSync(Job):
|
||||
dryrun = BooleanVar(
|
||||
default=True,
|
||||
description="Nur berichten, nichts schreiben (empfohlen fuer den ersten Lauf).",
|
||||
)
|
||||
|
||||
class Meta:
|
||||
name = "CF Estate Sync"
|
||||
description = "Cloudflare-Estate (Tunnel/Worker/Access/Hyperdrive/VPC) nach Nautobot syncen (Doku-SSoT)."
|
||||
grouping = "TP44 / Cloudflare"
|
||||
# Nur ein Boolean (dryrun) als Eingabe; das CF-Token kommt aus dem Nautobot-Secret,
|
||||
# nicht aus einer Job-Variable. Ohne diese Zeile (Default True) verweigert Nautobot
|
||||
# das Scheduling: "Job may have sensitive input variables".
|
||||
has_sensitive_variables = False
|
||||
|
||||
# ---------------------------------------------------------------- CF-Pull
|
||||
def _cf_get(self, path):
|
||||
"""(ok, result) — 403/404 werden abgefangen, damit fehlende Scopes den Sync nicht kippen."""
|
||||
req = urllib.request.Request(CF_API + path, headers={"Authorization": "Bearer " + self._tok})
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as r:
|
||||
return True, json.load(r).get("result")
|
||||
except urllib.error.HTTPError as e:
|
||||
if e.code in (403, 404):
|
||||
return False, None
|
||||
raise
|
||||
|
||||
def _ingress(self, tunnel_id):
|
||||
ok, res = self._cf_get(f"/accounts/{CF_ACCOUNT_ID}/cfd_tunnel/{tunnel_id}/configurations")
|
||||
rules = ((res or {}).get("config") or {}).get("ingress") or [] if ok else []
|
||||
return [r["hostname"] for r in rules if r.get("hostname")]
|
||||
|
||||
def _load_cf(self):
|
||||
"""networks/services: (name, rt_name, desc, extra, match_key, match_val).
|
||||
routes: {tunnel_id: cidr}. skipped: Liste nicht lesbarer Endpoints."""
|
||||
A = CF_ACCOUNT_ID
|
||||
networks, services, routes, skipped = [], [], {}, []
|
||||
|
||||
_, tuns = self._cf_get(f"/accounts/{A}/cfd_tunnel?is_deleted=false&per_page=50")
|
||||
for t in (tuns or []):
|
||||
networks.append((t["name"], "Cloudflare cloudflared Tunnel",
|
||||
f"cloudflared-Tunnel ({len(t.get('connections') or [])} Connectoren)",
|
||||
{"tunnel_id": t["id"], "kind": "cloudflared",
|
||||
"connector_count": len(t.get("connections") or []),
|
||||
"ingress_hostnames": self._ingress(t["id"])},
|
||||
"tunnel_id", t["id"]))
|
||||
_, warps = self._cf_get(f"/accounts/{A}/warp_connector?per_page=50")
|
||||
for w in (warps or []):
|
||||
networks.append((w["name"], "Cloudflare WARP Connector",
|
||||
f"WARP-Connector ({len(w.get('connections') or [])} Connectoren)",
|
||||
{"tunnel_id": w["id"], "kind": "warp_connector",
|
||||
"connector_count": len(w.get("connections") or [])},
|
||||
"tunnel_id", w["id"]))
|
||||
|
||||
_, scripts = self._cf_get(f"/accounts/{A}/workers/scripts")
|
||||
for s in (scripts or []):
|
||||
services.append((s["id"], "Cloudflare Worker", "Cloudflare Worker",
|
||||
{"script": s["id"]}, "script", s["id"]))
|
||||
|
||||
ok, apps = self._cf_get(f"/accounts/{A}/access/apps?per_page=100")
|
||||
if not ok:
|
||||
skipped.append("access/apps")
|
||||
for a in (apps or []):
|
||||
services.append((a.get("name") or a.get("id"), "Cloudflare Access Application",
|
||||
f"Access-App ({a.get('type')})",
|
||||
{"id": a.get("id"), "type": a.get("type"), "domain": a.get("domain")},
|
||||
"id", a.get("id")))
|
||||
|
||||
ok, hyper = self._cf_get(f"/accounts/{A}/hyperdrive/configs")
|
||||
if not ok:
|
||||
skipped.append("hyperdrive/configs")
|
||||
for h in (hyper or []):
|
||||
origin = h.get("origin") or {}
|
||||
services.append((h.get("name") or h.get("id"), "Cloudflare Hyperdrive", "Hyperdrive-Config",
|
||||
{"id": h.get("id"), "database": origin.get("database"),
|
||||
"user": origin.get("user")}, "id", h.get("id")))
|
||||
|
||||
ok, vpc = self._cf_get(f"/accounts/{A}/connectivity/directory/services")
|
||||
if not ok:
|
||||
skipped.append("connectivity/directory/services")
|
||||
for v in (vpc or []):
|
||||
# VPC-Directory-Services tragen ihren Identifier in "service_id" (NICHT "id").
|
||||
sid = v.get("service_id") or v.get("id")
|
||||
services.append((v.get("name") or sid, "Cloudflare VPC Service", "VPC-Service",
|
||||
{"service_id": sid, "type": v.get("type")}, "service_id", sid))
|
||||
|
||||
ok, rts = self._cf_get(f"/accounts/{A}/teamnet/routes?per_page=50")
|
||||
if not ok:
|
||||
skipped.append("teamnet/routes")
|
||||
for r in (rts or []):
|
||||
if r.get("network") and r.get("tunnel_id"):
|
||||
routes[r["tunnel_id"]] = r["network"]
|
||||
|
||||
return networks, services, routes, skipped
|
||||
|
||||
# ---------------------------------------------------------------- ORM-Upsert
|
||||
def _upsert(self, model, cache, name, rt, acct, desc, extra, mk, mv, label, seen, dryrun):
|
||||
# mv=None nie matchen: sonst greift .get(mk)==None auf das erste Objekt OHNE mk
|
||||
# und mischt fremde extra_config zusammen (z.B. VPC ohne id -> App-Launcher).
|
||||
if mv is None:
|
||||
self.logger.warning("~ %s '%s' ohne Match-Wert (%s) — uebersprungen", label, name, mk)
|
||||
return None
|
||||
ex = next((o for o in cache if (o.extra_config or {}).get(mk) == mv), None)
|
||||
if ex:
|
||||
seen.add(ex.id)
|
||||
merged = dict(ex.extra_config or {})
|
||||
merged.update(extra)
|
||||
if merged == (ex.extra_config or {}):
|
||||
return ex
|
||||
if dryrun:
|
||||
self.logger.info("~ %s '%s': extra_config-Update [DRY-RUN]", label, ex.name)
|
||||
return ex
|
||||
ex.extra_config = merged
|
||||
ex.validated_save()
|
||||
self.logger.info("~ %s '%s' aktualisiert", label, ex.name)
|
||||
return ex
|
||||
if dryrun:
|
||||
self.logger.info("+ %s '%s' [DRY-RUN]", label, name)
|
||||
return None
|
||||
obj = model(name=name, cloud_resource_type=rt, cloud_account=acct,
|
||||
description=desc, extra_config=extra)
|
||||
obj.validated_save()
|
||||
cache.append(obj)
|
||||
seen.add(obj.id)
|
||||
self.logger.info("+ %s '%s' angelegt", label, name)
|
||||
return obj
|
||||
|
||||
def _mark_stale(self, cache, seen, label, dryrun):
|
||||
for o in cache:
|
||||
extra = o.extra_config or {}
|
||||
if o.id in seen:
|
||||
if extra.get("stale") and not dryrun:
|
||||
o.extra_config = {k: v for k, v in extra.items() if k != "stale"}
|
||||
o.validated_save()
|
||||
self.logger.info("↩ %s '%s': stale entfernt (wieder da)", label, o.name)
|
||||
continue
|
||||
if extra.get("stale"):
|
||||
continue
|
||||
if dryrun:
|
||||
self.logger.warning("⚠ %s '%s' fehlt im Live-Pull → wuerde stale markiert [DRY-RUN]", label, o.name)
|
||||
continue
|
||||
o.extra_config = dict(extra, stale=True)
|
||||
o.validated_save()
|
||||
self.logger.warning("⚠ %s '%s' als stale markiert (nicht mehr in CF)", label, o.name)
|
||||
|
||||
# ---------------------------------------------------------------- run
|
||||
def run(self, dryrun=True):
|
||||
try:
|
||||
self._tok = Secret.objects.get(name="cf-api-token").get_value()
|
||||
except Secret.DoesNotExist:
|
||||
raise RuntimeError("Nautobot-Secret 'cf-api-token' fehlt — bitte anlegen (Provider env, CF_API_TOKEN).")
|
||||
|
||||
networks, services, routes, skipped = self._load_cf()
|
||||
self.logger.info("Live-Pull: %d CloudNetworks · %d CloudServices%s",
|
||||
len(networks), len(services),
|
||||
f" · SKIP: {', '.join(skipped)}" if skipped else " · alle Endpoints lesbar")
|
||||
|
||||
mfr, _ = Manufacturer.objects.get_or_create(name="Cloudflare")
|
||||
acct, created = CloudAccount.objects.get_or_create(
|
||||
name="Cloudflare", defaults={"account_number": CF_ACCOUNT_ID, "provider": mfr})
|
||||
if created and dryrun:
|
||||
pass
|
||||
|
||||
rt_by_name = {}
|
||||
for name, app, model_name in RESOURCE_TYPES:
|
||||
ct = ContentType.objects.get(app_label=app, model=model_name)
|
||||
rt, _ = CloudResourceType.objects.get_or_create(name=name, defaults={"provider": mfr})
|
||||
rt.content_types.add(ct)
|
||||
rt_by_name[name] = rt
|
||||
|
||||
net_cache = list(CloudNetwork.objects.filter(cloud_account=acct))
|
||||
svc_cache = list(CloudService.objects.filter(cloud_account=acct))
|
||||
seen_net, seen_svc = set(), set()
|
||||
|
||||
self.logger.info("[CloudNetworks]")
|
||||
for name, rt_name, desc, extra, mk, mv in networks:
|
||||
self._upsert(CloudNetwork, net_cache, name, rt_by_name.get(rt_name), acct,
|
||||
desc, extra, mk, mv, "CloudNetwork", seen_net, dryrun)
|
||||
|
||||
self.logger.info("[CloudServices]")
|
||||
for name, rt_name, desc, extra, mk, mv in services:
|
||||
self._upsert(CloudService, svc_cache, name, rt_by_name.get(rt_name), acct,
|
||||
desc, extra, mk, mv, "CloudService", seen_svc, dryrun)
|
||||
|
||||
self.logger.info("[Prefix-Assignments]")
|
||||
for tunnel_id, cidr in routes.items():
|
||||
net = next((n for n in net_cache if (n.extra_config or {}).get("tunnel_id") == tunnel_id), None)
|
||||
if not net or getattr(net, "id", None) is None:
|
||||
continue
|
||||
pfx = Prefix.objects.filter(prefix=cidr).first()
|
||||
if not pfx:
|
||||
self.logger.info(" ~ Route %s: Prefix nicht in IPAM → uebersprungen", cidr)
|
||||
continue
|
||||
if CloudNetworkPrefixAssignment.objects.filter(cloud_network=net, prefix=pfx).exists():
|
||||
continue
|
||||
if dryrun:
|
||||
self.logger.info(" + %s ↔ %s [DRY-RUN]", net.name, cidr)
|
||||
continue
|
||||
CloudNetworkPrefixAssignment(cloud_network=net, prefix=pfx).validated_save()
|
||||
self.logger.info(" + %s ↔ %s angelegt", net.name, cidr)
|
||||
|
||||
self.logger.info("[Verwaisung]")
|
||||
# CloudServices nur markieren, wenn ALLE Service-Endpoints lesbar waren — sonst
|
||||
# wuerde ein fehlender Scope echte Objekte faelschlich als verwaist markieren.
|
||||
svc_safe = not ({"access/apps", "hyperdrive/configs", "connectivity/directory/services"} & set(skipped))
|
||||
if svc_safe:
|
||||
self._mark_stale(svc_cache, seen_svc, "CloudService", dryrun)
|
||||
else:
|
||||
self.logger.info(" · CloudService-Verwaisung uebersprungen (Token-Scope unvollstaendig)")
|
||||
self._mark_stale(net_cache, seen_net, "CloudNetwork", dryrun)
|
||||
|
||||
self.logger.info("Fertig (%s).", "DRY-RUN — nichts geschrieben" if dryrun else "COMMIT")
|
||||
|
||||
|
||||
# Registrierung zentral in jobs/__init__.py (Nautobot importiert nur das Paket).
|
||||
@@ -52,15 +52,19 @@ BENUTZUNG
|
||||
python3 nautobot/gc-configplan.py --job drift-cleanup --apply --no-deploy
|
||||
"""
|
||||
import argparse
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from datetime import datetime
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
|
||||
# Status-UUIDs fuer nautobot_golden_config.configplan (2026-08-16 aus der API gelesen)
|
||||
@@ -160,8 +164,22 @@ def create_plans(device_names, config_set, change_control_id, dry):
|
||||
# Sammelabfrage zuordnen (Lehre aus TP21.1: REST sortiert alphabetisch).
|
||||
q = urllib.parse.urlencode({"change_control_id": change_control_id, "depth": 1})
|
||||
found = req("GET", f"/plugins/golden-config/config-plan/?{q}").get("results", [])
|
||||
|
||||
# Mehrere Plaene unter derselben ccid sind normal, sobald ein Job mit geaendertem
|
||||
# Inhalt erneut laeuft -- die ccid benennt die Aenderung, nicht den Versuch.
|
||||
# 🔴 Wer dann die REST-Reihenfolge entscheiden laesst, nimmt nondeterministisch
|
||||
# den ALTEN Plan und laeuft in den config_set-Guard unten (am 2026-09-21 genau so
|
||||
# passiert: Abbruch trotz korrekt erzeugtem neuen Plan). Der Job hat eben einen
|
||||
# neuen Plan angelegt, also gilt der juengste -- die alten bleiben Historie.
|
||||
def created(p):
|
||||
raw = str(p.get("created") or "")
|
||||
try:
|
||||
return datetime.fromisoformat(raw.replace("Z", "+00:00")).replace(tzinfo=None)
|
||||
except ValueError:
|
||||
return datetime.min
|
||||
|
||||
by_name = {}
|
||||
for p in found:
|
||||
for p in sorted(found, key=created):
|
||||
name = (p.get("device") or {}).get("display")
|
||||
by_name[name] = p["id"]
|
||||
|
||||
@@ -289,7 +307,7 @@ def job_mgmt_acl():
|
||||
das ist auf dem Core passiert. Umgekehrt fuhren branch/ap1/ap2 ssh/www/winbox/api/ftp/
|
||||
telnet komplett OHNE Address-Beschraenkung; winbox war auf allen vier offen.
|
||||
|
||||
KEIN LOCKOUT-RISIKO: der Deploy laeuft von Nautobot (10.40.20.30) aus, also aus
|
||||
KEIN LOCKOUT-RISIKO: der Deploy laeuft von Nautobot (10.40.20.154) aus, also aus
|
||||
10.40.0.0/16 -- die Adresse bleibt in jeder gesetzten ACL enthalten. RouterOS wendet
|
||||
`address=` ohnehin erst auf NEUE Verbindungen an, die laufende Session ueberlebt.
|
||||
|
||||
@@ -353,10 +371,367 @@ def job_tp42_dhcp_domain():
|
||||
return [(["crs326-core"], core_set, "TP42-dhcp-domain-bf")]
|
||||
|
||||
|
||||
def job_tp38_oob():
|
||||
"""TP38 (2026-09-15): OOB-MGMT-Port (ether23 untagged VLAN99) + Branch-Input-Firewall.
|
||||
|
||||
Zwei Geraete, zwei config_sets:
|
||||
* branch: ether23 untaggen, VLAN99 um untagged=ether23 erweitern, 8 Input-Regeln
|
||||
(deny-by-default). Die Regeln werden in der Reihenfolge angelegt, in der das
|
||||
Template sie rendert — established zuerst, drop zuletzt — damit kein LOCKOUT:
|
||||
die laufende Nautobot-SSH-Session (10.40.20.154) matcht auf Regel 1 (established),
|
||||
bevor Regel 8 (drop) greift.
|
||||
* core: ether23 untaggen, VLAN99 untagged=ether23, EINE Regel "OOB->core mgmt" mit
|
||||
place-before vor die Log-Regel (sonst landet sie hinter dem finalen drop und
|
||||
waere wirkungslos + non-compliant).
|
||||
|
||||
KEIN LOCKOUT-RISIKO: ether23 ist auf beiden Geraeten frei (running=false); der
|
||||
VLAN99-Umbau aendert nur `untagged`, nicht `tagged`; der Deploy laeuft von Nautobot
|
||||
(10.40.20.154), das in jeder gesetzten Firewall-Freigabe enthalten bleibt.
|
||||
"""
|
||||
bridge_set = (
|
||||
':do { /interface bridge port set [find interface=ether23] pvid=99 '
|
||||
'frame-types=admit-only-untagged-and-priority-tagged comment="TP38-OOB-MGMT" } '
|
||||
'on-error={ :log info "tp38: ether23 set geworfen" }'
|
||||
)
|
||||
vlan_set = (
|
||||
':do { /interface bridge vlan set [find vlan-ids=99] untagged=ether23 } '
|
||||
'on-error={ :log info "tp38: vlan99 untagged geworfen" }'
|
||||
)
|
||||
|
||||
branch_fw = "\n".join([
|
||||
'add action=accept chain=input comment="TP38: established" connection-state=established,related,untracked',
|
||||
'add action=drop chain=input comment="TP38: invalid" connection-state=invalid',
|
||||
'add action=accept chain=input comment="TP38: icmp" protocol=icmp',
|
||||
'add action=accept chain=input comment="TP38: mndp" dst-port=5678 protocol=udp',
|
||||
'add action=accept chain=input comment="TP38: mgmt LAN" dst-port=22,80,8291,8728 protocol=tcp src-address=10.40.0.0/16',
|
||||
'add action=accept chain=input comment="TP38: mgmt mesh" dst-port=22,80,8291,8728 protocol=tcp src-address=100.96.0.0/12',
|
||||
'add action=log chain=input comment="TP38: input log-only" log-prefix=TP38-BRANCH-WOULD-DROP',
|
||||
'add action=drop chain=input comment="TP38: input deny-by-default"',
|
||||
])
|
||||
branch_set = "\n".join([
|
||||
bridge_set,
|
||||
vlan_set,
|
||||
*[f"/ip firewall filter {ln}" for ln in branch_fw.splitlines()],
|
||||
':log info ("tp38: branch pvid=" . [:tostr [/interface bridge port get [find interface=ether23] pvid]]'
|
||||
' . " fw=" . [:tostr [:len [/ip firewall filter find where comment~"TP38"]]] . "/8")',
|
||||
])
|
||||
|
||||
# ⚠️ `add ... place-before=[find ...]` wird ueber Netmiko IGNORIERT (Regel landet am
|
||||
# Ketten-Ende, also HINTER dem finalen drop = wirkungslos; beim Deploy 2026-09-15 so
|
||||
# aufgeschlagen). Deshalb: erst add (appends), dann `move` vor die Log-Regel.
|
||||
core_set = "\n".join([
|
||||
bridge_set,
|
||||
vlan_set,
|
||||
'/ip firewall filter add action=accept chain=input comment="TP38: OOB->core mgmt" '
|
||||
'dst-port=22,80,8291,8728 in-interface=vlan99 protocol=tcp',
|
||||
'/ip firewall filter move [find where comment="TP38: OOB->core mgmt"] '
|
||||
'destination=[find where comment="TP5: input log-only"]',
|
||||
':log info ("tp38: core pvid=" . [:tostr [/interface bridge port get [find interface=ether23] pvid]]'
|
||||
' . " oob=" . [:tostr [:len [/ip firewall filter find where comment="TP38: OOB->core mgmt"]]])',
|
||||
])
|
||||
|
||||
return [
|
||||
(["crs326-branch"], branch_set, "TP38-oob-branch"),
|
||||
(["crs326-core"], core_set, "TP38-oob-core"),
|
||||
]
|
||||
|
||||
|
||||
def job_tp45_agh_fw():
|
||||
"""TP45 Schicht A (2026-09-21): DNS-Freigaben fuer die neuen Resolver-Adressen .30/.31.
|
||||
|
||||
Der Resolver zieht von .36/.37 auf .30/.31 (TP32-Oktett-Klasse DNS `.3x`). Damit die
|
||||
Umstellung ohne Ausfall geht, laufen beide Adressen kurz parallel — also braucht der
|
||||
Core **beide** Freigaben. Diese hier ist ADDITIV: die vier .36/.37-Regeln bleiben
|
||||
stehen und fallen erst beim Rueckbau (eigener Schritt, wenn die Zaehler stillstehen).
|
||||
|
||||
Die vier Regeln sind ein Literal im Soll (`local_config_context_data.firewall_filter`,
|
||||
gepflegt mit `nautobot/tp45-agh-firewall.py`) — der Job zieht nur das Geraet nach.
|
||||
|
||||
🔴 `place-before` wird ueber Netmiko IGNORIERT (Lehre aus TP38, 2026-09-15): die Regel
|
||||
landet am Ketten-Ende, also HINTER dem finalen `drop` und waere wirkungslos, waehrend
|
||||
der Plan trotzdem „Completed" meldet. Deshalb `add` (appends) und danach `move` vor
|
||||
die Log-Regel — in der Reihenfolge, in der die Regeln stehen sollen (jedes `move`
|
||||
schiebt seine Regel unmittelbar vor das Ziel, also hinter die zuvor verschobenen).
|
||||
|
||||
🔴 **Zweite Falle, am 2026-09-21 beim ersten Lauf aufgeschlagen:** nicht der `move`
|
||||
war falsch, sondern der **Selektor** — `find where dst-address=<bare-IP>` matcht nie
|
||||
(Details und Tragweite in der Docstring von `sel()` unten). Der Plan meldete
|
||||
trotzdem „Completed", die Regeln lagen hinter dem `drop`. Deshalb ist die Abnahme
|
||||
hier **nicht** der Plan-Status, sondern die Position am Geraet.
|
||||
|
||||
KEIN LOCKOUT-RISIKO: die Regeln erweitern nur `accept` in der forward-Kette; der
|
||||
Mgmt-Pfad (Nautobot-SSH -> vlan99) ist von der Aenderung nicht beruehrt. Der Deploy
|
||||
laeuft von 10.40.20.154, das auf Regel 22 (established) matcht.
|
||||
"""
|
||||
ips = ["10.40.20.30", "10.40.20.31"]
|
||||
log = 'TP5: log-only, Vorstufe zum deny-by-default'
|
||||
|
||||
def sel(cmt, ip):
|
||||
"""Selektor auf genau eine Regel.
|
||||
|
||||
🔴 **`dst-address=<bare-IP>` matcht in `find where` NIE** — live am Core
|
||||
gemessen (2026-09-21): `find where dst-address=10.40.20.30` → **0 Treffer**,
|
||||
auch mit `/32`; `dst-address~"10.40.20.30"` (Regex) → 2. Deshalb steht hier
|
||||
`~`, nicht `=`.
|
||||
|
||||
⚠️ Die Tragweite ist größer als dieser Job: **eine Prüfung, die 0 meldet,
|
||||
sieht wie „nicht vorhanden" aus und nicht wie „kaputt".** Genau daran ist
|
||||
der erste Lauf dieses Jobs gescheitert — der `add` lief (der Guard sah
|
||||
„nicht vorhanden"), der `move` lief ins Leere, und der Plan meldete
|
||||
trotzdem „Completed". Dieselbe Zeile stand als *Verifikation* in
|
||||
`docs/transaktionen/TP23-agh-firewall.commands.rsc:7` und hat dort seit
|
||||
dem 2026-08-25 immer `0` gemeldet, ohne je etwas zu prüfen.
|
||||
Gleiche Klasse wie `nc -z` (2026-09-01) und das `sed`-Maskierungsmuster:
|
||||
**erst den Selektor verdächtigen, dann das Gerät.**
|
||||
"""
|
||||
return f'comment="{cmt}" dst-address~"{ip}"'
|
||||
|
||||
def rule(ip, proto, cmt):
|
||||
return (f'/ip firewall filter add action=accept chain=forward comment="{cmt}" '
|
||||
f'dst-address={ip} dst-port=53 protocol={proto}')
|
||||
|
||||
lines = []
|
||||
for ip in ips:
|
||||
# Guard + move getrennt: nur der Guard ist idempotent (ein zweiter Lauf
|
||||
# verdoppelt die Regeln nicht), der move läuft immer — er ist auch der
|
||||
# Reparaturweg, wenn eine Regel schon existiert, aber falsch einsortiert ist.
|
||||
for proto, cmt in (("udp", "TP5-E2: LAN->AGH dns"), ("tcp", "TP5-E2: LAN->AGH dns-tcp")):
|
||||
lines.append(f':if ([:len [/ip firewall filter find where {sel(cmt, ip)}]] = 0) '
|
||||
f'do={{ {rule(ip, proto, cmt)} }}')
|
||||
for ip in ips:
|
||||
for cmt in ("TP5-E2: LAN->AGH dns", "TP5-E2: LAN->AGH dns-tcp"):
|
||||
lines.append(f'/ip firewall filter move [find where {sel(cmt, ip)}] '
|
||||
f'destination=[find where comment="{log}"]')
|
||||
# Der Anker ist bewusst die Log-Regel (Baseline, stabil) und nicht `caddy`: haengt
|
||||
# sie unmittelbar vor der Log-Regel, landen die vier Regeln DAHINTER -- der AGH-Block
|
||||
# bleibt so zusammen, wie das Soll ihn fuehrt. Waehlte man `caddy` als Ziel und die
|
||||
# Regel fehlte, laegen die vier hinter dem `drop` = funktional kaputt; so bleibt im
|
||||
# Fehlerfall nur eine Reihenfolge-Abweichung (Compliance-Drift, kein Ausfall).
|
||||
lines.append(f'/ip firewall filter move [find where comment="TP31: AP-cloudflared -> caddy-bf1"] '
|
||||
f'destination=[find where comment="{log}"]')
|
||||
# ⚠️ Diese Zeile meldet NUR Anwesenheit (je IP 2 Regeln erwartet), NICHT die
|
||||
# Position -- sie kann also gruen aussehen, waehrend die Regeln hinter dem `drop`
|
||||
# liegen. Genau daran ist der erste Lauf gescheitert. **Die Abnahme ist der
|
||||
# Geraete-Read nach dem Deploy** (`print terse proplist=comment,packets`).
|
||||
lines.append(
|
||||
':log info ("tp45-agh-fw: .30=" . '
|
||||
'[:tostr [:len [/ip firewall filter find where dst-address~"10.40.20.30"]]] . " .31=" . '
|
||||
'[:tostr [:len [/ip firewall filter find where dst-address~"10.40.20.31"]]] . " log=" . '
|
||||
f'[:tostr [:len [/ip firewall filter find where comment="{log}"]]])')
|
||||
|
||||
return [(["crs326-core"], "\n".join(lines), "TP45-agh-firewall")]
|
||||
|
||||
|
||||
def job_tp45_agh_dhcp():
|
||||
"""TP45 Schicht A (2026-09-21): DHCP-DNS-Server der 7 Netze .36,.37 -> .30,.31.
|
||||
|
||||
Der Core verteilt in ALLEN sieben Netzen `dns-server=10.40.20.36,10.40.20.37`
|
||||
(live geprueft 2026-09-21). Das Template (Z.137) ist auf .30,.31 gehoben; dieser Job
|
||||
zieht das laufende Geraet nach.
|
||||
|
||||
MODIFY, kein add: das Netz existiert -> `set [find]`, nicht die `add`-Zeile der
|
||||
intended config (die wuerfe `already have such address`). `[find]` ohne Filter
|
||||
trifft alle 7 Netze. Gleiche Form wie `job_tp42_dhcp_domain`.
|
||||
|
||||
⚠️ **Reihenfolge zwingend:** erst `tp45-agh-fw` deployen. Sonst liefert DHCP Adressen
|
||||
aus, die die Firewall noch nicht durchlaesst — unter deny-by-default faellt DNS aus
|
||||
allen VLANs aus.
|
||||
|
||||
KEIN LOCKOUT-RISIKO: eine DHCP-Option beruehrt weder Routing noch Mgmt-Zugang;
|
||||
Clients ziehen sie erst beim naechsten Lease-Renew (lease-time 1d, GUEST 12h).
|
||||
"""
|
||||
core_set = "\n".join([
|
||||
':do { /ip dhcp-server network set [find] dns-server=10.40.20.30,10.40.20.31 } '
|
||||
'on-error={ :log info "tp45-agh-dhcp: set geworfen" }',
|
||||
':log info ("tp45-agh-dhcp: auf-neu=" . [:tostr [:len [/ip dhcp-server network '
|
||||
'find where dns-server~"10.40.20.30"]]] . "/7")',
|
||||
])
|
||||
return [(["crs326-core"], core_set, "TP45-agh-dhcp")]
|
||||
|
||||
|
||||
def _pool_set(vlan, ranges):
|
||||
"""Zeilen, die `pool-v<vlan>` auf `ranges` ziehen -- mit gezaehltem Selektor.
|
||||
|
||||
MODIFY, kein add: der Pool existiert, die `add`-Zeile der intended config
|
||||
wuerfe `already have such name`. Gleiche Form wie `job_tp45_agh_dhcp`.
|
||||
|
||||
🔴 Der Selektor wird GEZAEHLT, nicht geglaubt (Lehre vom 2026-09-21): ein
|
||||
`find where`, das 0 trifft, sieht wie „nicht vorhanden" aus und nicht wie
|
||||
„kaputt" -- ein blankes `set [find where name=…]` liefe ins Leere und der
|
||||
Job meldete trotzdem Erfolg (dieselbe Klasse wie `dst-address=<bare IP>`
|
||||
in TP23-agh-firewall.commands.rsc:7, das seit dem 08-25 immer 0 meldete).
|
||||
|
||||
🔴🔴 **JEDES Statement muss auf EINER Zeile klammer-vollstaendig sein.**
|
||||
Erste Fassung hatte `:if (...) do={` am Zeilenende -- RouterOS geht dann in
|
||||
den Fortsetzungs-Modus und zeigt einen ANDEREN Prompt, der netmiko-Treiber
|
||||
verliert sein Muster und der Deploy stirbt mit
|
||||
`E1015: Pattern not detected: (redacted)@crs326\\-core\\].*$|#.*$)`.
|
||||
Der Plan stand danach auf `Failed`, **ohne** etwas geschrieben zu haben.
|
||||
Deshalb ist jede Zeile hier ein vollstaendiges `… do={ …; … } else={ … }`.
|
||||
"""
|
||||
pool = f"pool-v{vlan}"
|
||||
return "\n".join([
|
||||
f':if ([:len [/ip pool find where name="{pool}"]] = 1) do={{ '
|
||||
f'/ip pool set [find where name="{pool}"] ranges={ranges}; '
|
||||
f':log info ("tp45-pool: {pool} -> " . [/ip pool get [find where name="{pool}"] ranges]) }} '
|
||||
f'else={{ :log info ("tp45-pool: ABBRUCH, {pool} nicht eindeutig") }}',
|
||||
])
|
||||
|
||||
|
||||
def job_tp45_pool_v20():
|
||||
"""TP45 (2026-09-21): pool-v20 von .100-.200 auf .200-.249 verengen.
|
||||
|
||||
Damit werden die Schema-Baender .100-.149 (Server-Hosts) und .150-.199
|
||||
(App-Services) in VLAN20 echt frei. VLAN20 traegt KEINE DHCP-Reservierung
|
||||
im alten Fenster (live geprueft: nur `nas-bf .20.10` in .10x) und 0
|
||||
dynamische Leases -- die Verengung kostet hier nichts.
|
||||
|
||||
🔴 Warum ueberhaupt: `nautobot .154-.158`, `gitea .157`, `pve-mcp .173` und
|
||||
`shelly-mcp .180` tragen ihre IP GERAETELOKAL und stehen in KEINEM
|
||||
Lease-Eintrag -- der Core kann sie nicht schuetzen und einem DHCP-Client
|
||||
.154 anbieten. Die Verengung ist hier sofort wirksam.
|
||||
|
||||
⚠️ VLAN30 gehoert NICHT hierher: dort liegen 35 statische Reservierungen IM
|
||||
Fenster .200-.249 (26 Shellys .202-.226/.233, SwitchBot .227, Twinkly .228,
|
||||
Withings .230/.231, lichterketten .236/.237, Nuki .241/.244, CCU .232).
|
||||
Erst Schicht B zieht die um, DANN pool-v30.
|
||||
"""
|
||||
return [(["crs326-core"], _pool_set(20, "10.40.20.200-10.40.20.249"), "TP45-pool-v20")]
|
||||
|
||||
|
||||
def _tp45_leases_module():
|
||||
"""MOVES/REMOVE/ADDS aus `tp45-leases.py` laden.
|
||||
|
||||
🔴 **NICHT kopieren.** Diese Tabellen sind die SSoT des Umzugs. Eine zweite
|
||||
Kopie hier liefe beim ersten Edit auseinander — und zwar still, weil beide
|
||||
Seiten fuer sich plausibel aussehen; am Geraet faellt nur auf, dass eine
|
||||
Adresse fehlt.
|
||||
|
||||
Der Dateiname traegt einen Bindestrich (Repo-Konvention fuer Skripte), ist
|
||||
also nicht regulaer importierbar -> importlib ueber den Pfad. `__name__` ist
|
||||
dann nicht `__main__`, das `main()`-Guard der Datei greift also nicht.
|
||||
"""
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"tp45_leases", pathlib.Path(__file__).with_name("tp45-leases.py"))
|
||||
mod = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(mod)
|
||||
return mod
|
||||
|
||||
|
||||
def _lease_move(mac, soll):
|
||||
"""Eine Umzugs-Zeile — EINZEILIG und klammer-vollstaendig (E1015-Lehre, s. _pool_set).
|
||||
|
||||
Der Selektor wird GEZAEHLT: ein `find where`, das 0 trifft, sieht wie
|
||||
„nicht vorhanden" aus und nicht wie „kaputt", und ein blankes `set [find …]`
|
||||
liefe ins Leere, waehrend der Job Erfolg meldet.
|
||||
|
||||
Absichtlich OHNE `address=<ist>` im Selektor: ein zweiter Lauf muss ein
|
||||
No-Op sein, kein Fehler. Die Ist-Adresse wird stattdessen VOR dem Deploy
|
||||
geprueft — `tp45-leases.py move` verlangt, dass das Soll genau `ist` traegt
|
||||
und bricht sonst ab.
|
||||
|
||||
🔴🔴 **ZWEI VERSCHIEDENE Selektor-Formen, und das ist keine Kosmetik.**
|
||||
Die erste Fassung schrieb ueberall die Argument-Form `[… [find where …]]`.
|
||||
Die ist **Syntaxfehler**, sobald sie in `[:len …]` steht:
|
||||
`expected command name (line 1 column 55)` (live gemessen 2026-09-21)
|
||||
Der Deploy lief trotzdem als `Completed` durch, das Geraet blieb
|
||||
**unveraendert**, und im Log stand nur die Schlusszeile — von den 38
|
||||
`:if`-Statements hat **kein einziger** Zweig geloggt, also ist auch kein
|
||||
`set` gelaufen.
|
||||
|
||||
* in `[:len …]` und direkt vor `find`: **`/ip dhcp-server lease find where …`**
|
||||
(kein Klammerpaar um `find`) — Muster aus `_pool_set`/`job_tp45_agh_dhcp`
|
||||
* als Argument einer Vollpfad-Kommandozeile: `set [find where …]`
|
||||
— diese Form ist in `_pool_set` bewiesen und bleibt.
|
||||
|
||||
⚠️ **Merkregel:** `Completed` am Plan und `SUCCESS` am Job beweisen hier
|
||||
nichts. Der Beweis ist die **Zahl der Log-Zeilen** (jeder Zweig loggt) oder
|
||||
ein Geraete-Read. Beides gehoert zur Abnahme — genau wie beim
|
||||
`pool-v20`-Deploy, der ohne Gegenprobe als gelungen gegolten haette.
|
||||
"""
|
||||
find = f'find where mac-address={mac}'
|
||||
arg = f'[find where mac-address={mac}]'
|
||||
return (f':if ([:len [/ip dhcp-server lease {find}]] = 1) do={{ '
|
||||
f'/ip dhcp-server lease set {arg} address={soll}; '
|
||||
f':log info ("tp45-v30: {mac} -> {soll}") }} '
|
||||
f'else={{ :log info ("tp45-v30: {mac} NICHT EINDEUTIG -- nicht angefasst") }}')
|
||||
|
||||
|
||||
def job_tp45_v30_leases():
|
||||
"""TP45 Schicht B2 (2026-09-21): die VLAN30-Reservierungen ins Oktett-Schema.
|
||||
|
||||
36 Reservierungen wandern aus dem DHCP-Pool-Fenster (.202-.244, .5, .8) in die
|
||||
Schema-Baender: `.11-.14` fest verbaute Infrastruktur, `.151-.176` Shelly-Block,
|
||||
`.177-.196` App-Services. Dazu zwei Sonderfaelle:
|
||||
|
||||
* `pve-homematic-raspi-ccu` (MAC BC:24:11:8E:0A:57) wird ENTFERNT — eine
|
||||
Leiche aus dem Core-Cutover (`status=waiting`, `last-seen=never`). Die CCU
|
||||
ist live in VLAN20 (.20.232, ARP + HTTP 200 belegt, Caddys `ccu.bf` proxyt
|
||||
dorthin); die VLAN30-Reservierung ist ein Zombie.
|
||||
* `SONOFF-NSPanelPro` hat GAR KEINE Reservierung, nur eine dynamische Lease
|
||||
auf `.249`. Wird per `make-static` gepinnt und auf `.12` gezogen.
|
||||
|
||||
⚠️ **Reihenfolge:** dieser Job gehoert VOR `tp45-pool-v30`. Die Verengung auf
|
||||
`.200-.249` setzt voraus, dass keine Reservierung mehr im Schema-Band liegt —
|
||||
sonst zieht der Pool Adressen, die schon vergeben sind.
|
||||
|
||||
⚠️ **Kein Ausfall, kein Lockout:** ueber DHCP-Reservierungen umzuziehen heisst,
|
||||
dass die Geraete beim naechsten Lease-Renew selbst nachziehen (lease-time 1d).
|
||||
Nur zwei der 36 sind im Moment ueberhaupt `bound` (SwitchBot, DTUBI); alle
|
||||
anderen stehen auf `last-seen=never`. Nichts wird hart umgeschaltet.
|
||||
|
||||
⚠️ **`.150` (Ender3V3KE) und `.180` (espressif/Airfryer) bleiben, wo sie sind** —
|
||||
sie liegen bereits im App-Band und werden bewusst nicht bewegt.
|
||||
|
||||
Rueckweg: `MOVES` traegt Ist UND Soll je MAC, das inverse `set` ist also
|
||||
mechanisch; zusaetzlich liegt vor dem Deploy ein binaeres Backup auf dem Core.
|
||||
"""
|
||||
mod = _tp45_leases_module()
|
||||
lines = [_lease_move(mac, soll) for mac, (_ist, soll) in sorted(mod.MOVES.items())]
|
||||
|
||||
for mac in sorted(mod.REMOVE):
|
||||
find = f'find where mac-address={mac}'
|
||||
arg = f'[find where mac-address={mac}]'
|
||||
lines.append(f':if ([:len [/ip dhcp-server lease {find}]] = 1) do={{ '
|
||||
f'/ip dhcp-server lease remove {arg}; '
|
||||
f':log info ("tp45-v30: CCU-Leiche {mac} entfernt") }} '
|
||||
f'else={{ :log info ("tp45-v30: CCU-Leiche schon weg") }}')
|
||||
|
||||
for add in mod.ADDS:
|
||||
mac = re.search(r"mac-address=(\S+)", add).group(1)
|
||||
ip = re.search(r"address=(\S+)", add).group(1)
|
||||
cmt = re.search(r"comment=(\S+)", add).group(1)
|
||||
find = f'find where mac-address={mac}'
|
||||
arg = f'[find where mac-address={mac}]'
|
||||
# make-static VOR set: auf einer dynamischen Lease ist `set address=` wirkungslos
|
||||
# bzw. wird beim naechsten Renew ohnehin verworfen -- erst statisch, dann pinnen.
|
||||
lines.append(f':if ([:len [/ip dhcp-server lease {find}]] = 1) do={{ '
|
||||
f'/ip dhcp-server lease make-static {arg}; '
|
||||
f'/ip dhcp-server lease set {arg} address={ip} comment="{cmt}"; '
|
||||
f':log info ("tp45-v30: NSPanelPro statisch auf {ip}") }} '
|
||||
f'else={{ :log info ("tp45-v30: NSPanelPro hat keine Lease -- spaeter nachziehen") }}')
|
||||
|
||||
# Abnahme-Zeile. `address=` ist ein echter Eigenschaftsvergleich (kein Regex) --
|
||||
# bewusst NICHT `~"10.40.30.11"`, das traefe auch .110-.119.
|
||||
lines.append(
|
||||
':log info ("tp45-v30: .11=" . [:tostr [:len [/ip dhcp-server lease '
|
||||
'find where address=10.40.30.11]]] . " .12=" . [:tostr [:len [/ip dhcp-server '
|
||||
'lease find where address=10.40.30.12]]] . " .151=" . [:tostr [:len '
|
||||
'[/ip dhcp-server lease find where address=10.40.30.151]]] . " .196=" . '
|
||||
'[:tostr [:len [/ip dhcp-server lease find where address=10.40.30.196]]])')
|
||||
|
||||
return [(["crs326-core"], "\n".join(lines), "TP45-v30-leases")]
|
||||
|
||||
|
||||
JOBS = {
|
||||
"drift-cleanup": job_drift_cleanup,
|
||||
"mgmt-acl": job_mgmt_acl,
|
||||
"tp42-dhcp-domain": job_tp42_dhcp_domain,
|
||||
"tp38-oob": job_tp38_oob,
|
||||
"tp45-agh-fw": job_tp45_agh_fw,
|
||||
"tp45-agh-dhcp": job_tp45_agh_dhcp,
|
||||
"tp45-pool-v20": job_tp45_pool_v20,
|
||||
"tp45-v30-leases": job_tp45_v30_leases,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
NB = os.environ.get("NAUTOBOT_URL", "http://10.40.20.30:8080")
|
||||
NB = os.environ.get("NAUTOBOT_URL", "http://10.40.20.154:8080")
|
||||
GEN_JOB = "f93bfa53-2332-4fa2-a6ab-5921c802fcee" # Generate Config Plans
|
||||
DEPLOY_JOB = "31c3f093-eeec-43fa-ba4a-0aed2f3c67aa" # Deploy Config Plans
|
||||
|
||||
|
||||
199
nautobot/gc-snapshot.py
Normal file
199
nautobot/gc-snapshot.py
Normal file
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
TP36-Live — Live-Gerätedaten als CustomFields auf den RouterOS-dcim.Device.
|
||||
|
||||
Legt (idempotent, get-or-create) fünf CustomFields auf `dcim.device` an und
|
||||
befüllt sie für jedes RouterOS-Gerät. Die Werte kommen AUSSCHLIESSLICH aus
|
||||
Nautobot selbst — es wird KEIN Gerät kontaktiert:
|
||||
|
||||
ros_version aus der GoldenConfig-`backup_config` ("by RouterOS X.Y.Z")
|
||||
dhcp_server_count gezählte Einträge der Sektion "/ip dhcp-server" im Backup
|
||||
dns_upstream_mode aus der "/ip dns"-Sektion des Backups: doh | plain | none
|
||||
compliance_pct GC config-compliance: compliant / total * 100 (gerundet)
|
||||
mgmt_ip device.primary_ip4 (ohne /prefix)
|
||||
|
||||
Warum aus Nautobot statt live vom Gerät: der MGMT-/REST-Weg (VLAN99:80) ist
|
||||
nicht aus jedem Vantage erreichbar; die GC-Backups werden täglich (03:30 UTC,
|
||||
ScheduledJob "GC Backup+Compliance – taeglich") per SSH gezogen und sind die
|
||||
Doku-SSoT. Damit braucht der Snapshot keine Geräte-Credentials. Aktualität steht
|
||||
in `backup_last_success_date` — ist die alt, steht der Backup-Schedule (2026-09-12
|
||||
war er 8 Tage aus, weil es vorher gar keinen gab).
|
||||
|
||||
Der Backup-Config-Text wird nur INTERN geparst (Zähler/Modus abgeleitet) und
|
||||
NIE ausgegeben — er kann gescrubte Reste enthalten (PSK-Leak-Regel).
|
||||
|
||||
Token-Quelle: docs/nas-vlan20/stacks/210-nautobot.yml (gitignored),
|
||||
Schlüssel NAUTOBOT_SUPERUSER_API_TOKEN.
|
||||
|
||||
Aufruf:
|
||||
python3 nautobot/gc-snapshot.py # Dry-Run (default) — zeigt nur an
|
||||
python3 nautobot/gc-snapshot.py --commit # legt CustomFields an + patcht Devices
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import urllib.error
|
||||
from pathlib import Path
|
||||
|
||||
NB = os.environ.get("NAUTOBOT_URL", "http://10.40.20.154:8080")
|
||||
REPO = Path(__file__).resolve().parent.parent
|
||||
YML = REPO / "docs" / "nas-vlan20" / "stacks" / "210-nautobot.yml"
|
||||
COMMIT = "--commit" in sys.argv
|
||||
|
||||
|
||||
def load_token() -> str:
|
||||
for line in YML.read_text().splitlines():
|
||||
m = re.search(r"NAUTOBOT_SUPERUSER_API_TOKEN[=:]\s*([^\s\"']+)", line)
|
||||
if m:
|
||||
return m.group(1)
|
||||
sys.exit(f"NAUTOBOT_SUPERUSER_API_TOKEN nicht in {YML} gefunden")
|
||||
|
||||
|
||||
TOKEN = load_token()
|
||||
HDRS = {"Authorization": f"Token {TOKEN}", "Accept": "application/json",
|
||||
"Content-Type": "application/json"}
|
||||
|
||||
|
||||
def req(method: str, path: str, body=None):
|
||||
data = json.dumps(body).encode() if body is not None else None
|
||||
r = urllib.request.Request(f"{NB}{path}", data=data, headers=HDRS, method=method)
|
||||
try:
|
||||
with urllib.request.urlopen(r, timeout=20) as resp:
|
||||
return resp.status, json.loads(resp.read().decode() or "{}")
|
||||
except urllib.error.HTTPError as e:
|
||||
return e.code, json.loads(e.read().decode() or "{}")
|
||||
|
||||
|
||||
# --------------------------------------------------------------- CustomFields
|
||||
# (key, label, type, description). content_types wird ergänzt.
|
||||
CUSTOM_FIELDS = [
|
||||
("ros_version", "RouterOS-Version", "text",
|
||||
"RouterOS-Version aus der GC-backup_config (by RouterOS …)."),
|
||||
("compliance_pct", "GC-Compliance %", "integer",
|
||||
"Golden-Config config-compliance: compliant/total in Prozent."),
|
||||
("dhcp_server_count", "DHCP-Server", "integer",
|
||||
"Anzahl Einträge der Sektion /ip dhcp-server im GC-Backup."),
|
||||
("mgmt_ip", "Mgmt-IP", "text",
|
||||
"device.primary_ip4 ohne Prefix (VLAN99-Management)."),
|
||||
("dns_upstream_mode", "DNS-Upstream-Modus", "text",
|
||||
"Aus /ip dns des GC-Backups: doh | plain | none."),
|
||||
]
|
||||
CF_KEYS = [c[0] for c in CUSTOM_FIELDS]
|
||||
|
||||
|
||||
def ensure_custom_fields():
|
||||
"""get-or-create die fünf CFs; bei Bestand: content_type dcim.device sicherstellen."""
|
||||
st, d = req("GET", "/api/extras/custom-fields/?limit=0")
|
||||
have = {r["key"]: r for r in d.get("results", [])}
|
||||
for key, label, typ, desc in CUSTOM_FIELDS:
|
||||
ex = have.get(key)
|
||||
if ex:
|
||||
cts = ex.get("content_types", [])
|
||||
ok = "dcim.device" in cts
|
||||
print(f" = CF '{key}' existiert ({ex['id']}){'' if ok else ' ⚠ ohne dcim.device!'}")
|
||||
if not ok and COMMIT:
|
||||
req("PATCH", f"/api/extras/custom-fields/{ex['id']}/",
|
||||
{"content_types": sorted(set(cts) | {"dcim.device"})})
|
||||
print(f" + dcim.device ergänzt")
|
||||
continue
|
||||
payload = {"key": key, "label": label, "type": typ, "description": desc,
|
||||
"content_types": ["dcim.device"], "grouping": "TP36-Live",
|
||||
"required": False}
|
||||
if not COMMIT:
|
||||
print(f" + CF '{key}' ({typ}) [DRY-RUN]")
|
||||
continue
|
||||
st, r = req("POST", "/api/extras/custom-fields/", payload)
|
||||
print(f" {'+' if st in (200,201) else '!'} CF '{key}'"
|
||||
+ (f" angelegt ({r.get('id')})" if st in (200, 201)
|
||||
else f" FEHLER {st}: {json.dumps(r)[:200]}"))
|
||||
|
||||
|
||||
# --------------------------------------------------------------- Backup-Parser
|
||||
def parse_backup(cfg: str):
|
||||
"""Werte aus dem flachen RouterOS-Export (eine '/…'-Zeile pro Kommando)."""
|
||||
ver = ""
|
||||
m = re.search(r"by RouterOS ([0-9][0-9A-Za-z.\-]*)", cfg)
|
||||
if m:
|
||||
ver = m.group(1)
|
||||
# DHCP-Server = Zeilen '/ip dhcp-server add …' (NICHT lease/network/option)
|
||||
dhcp = len(re.findall(r"(?m)^\s*/ip dhcp-server add\b", cfg))
|
||||
# DNS-Modus aus der '/ip dns set …'-Zeile
|
||||
mode = "none"
|
||||
md = re.search(r"(?m)^\s*/ip dns set\b.*", cfg)
|
||||
if md:
|
||||
line = md.group(0)
|
||||
doh = re.search(r"use-doh-server=([^\s]+)", line)
|
||||
srv = re.search(r"\bservers=([^\s]+)", line)
|
||||
if doh and doh.group(1) not in ("", '""'):
|
||||
mode = "doh"
|
||||
elif srv and srv.group(1) not in ("", '""'):
|
||||
mode = "plain"
|
||||
return ver, dhcp, mode
|
||||
|
||||
|
||||
def compliance_pct(device_name: str):
|
||||
q = urllib.parse.urlencode({"device": device_name, "limit": 0, "depth": 0})
|
||||
st, d = req("GET", f"/api/plugins/golden-config/config-compliance/?{q}")
|
||||
rows = d.get("results", [])
|
||||
if not rows:
|
||||
return None
|
||||
ok = sum(1 for r in rows if r.get("compliance"))
|
||||
return round(ok / len(rows) * 100)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------- main
|
||||
def routeros_devices():
|
||||
q = urllib.parse.urlencode({"limit": 0, "depth": 1, "platform": "RouterOS"})
|
||||
st, d = req("GET", f"/api/dcim/devices/?{q}")
|
||||
devs = []
|
||||
for r in d.get("results", []):
|
||||
ip = r.get("primary_ip4")
|
||||
ip = ip.get("address") if isinstance(ip, dict) else ip
|
||||
if not ip:
|
||||
continue # tote Altgeräte (cudy/sw) ohne IP überspringen
|
||||
devs.append((r["id"], r["name"], ip.split("/")[0]))
|
||||
return sorted(devs, key=lambda x: x[1])
|
||||
|
||||
|
||||
def main():
|
||||
print(f"== TP36-Live gc-snapshot ({'COMMIT' if COMMIT else 'DRY-RUN'}) @ {NB} ==")
|
||||
|
||||
print("\n[1] CustomFields sicherstellen")
|
||||
ensure_custom_fields()
|
||||
|
||||
print("\n[2] Golden-Config-Backups laden")
|
||||
st, gc = req("GET", "/api/plugins/golden-config/golden-config/?depth=1&limit=0")
|
||||
backups = {}
|
||||
for r in gc.get("results", []):
|
||||
dev = r.get("device")
|
||||
dev = dev.get("name") if isinstance(dev, dict) else dev
|
||||
backups[dev] = (r.get("backup_config") or "", r.get("backup_last_success_date") or "?")
|
||||
|
||||
print("\n[3] Snapshot je Gerät")
|
||||
print(f" {'Gerät':16} {'ros':9} {'cmpl%':>5} {'dhcp':>4} {'dns':6} {'mgmt-ip':15} backup")
|
||||
for dev_id, name, mgmt in routeros_devices():
|
||||
cfg, when = backups.get(name, ("", "—"))
|
||||
ver, dhcp, dns = parse_backup(cfg) if cfg else ("?", 0, "?")
|
||||
pct = compliance_pct(name)
|
||||
vals = {
|
||||
"ros_version": ver or None,
|
||||
"compliance_pct": pct,
|
||||
"dhcp_server_count": dhcp,
|
||||
"mgmt_ip": mgmt,
|
||||
"dns_upstream_mode": dns,
|
||||
}
|
||||
pct_s = "—" if pct is None else str(pct)
|
||||
print(f" {name:16} {ver or '?':9} {pct_s:>5} {dhcp:>4} {dns:6} {mgmt:15} {when[:10]}")
|
||||
if COMMIT:
|
||||
st, r = req("PATCH", f"/api/dcim/devices/{dev_id}/", {"custom_fields": vals})
|
||||
if st not in (200, 201):
|
||||
print(f" ! PATCH FEHLER {st}: {json.dumps(r)[:200]}")
|
||||
|
||||
print(f"\n== fertig ({'COMMIT — geschrieben' if COMMIT else 'DRY-RUN — nichts geschrieben'}) ==")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -55,7 +55,7 @@ HIER = os.path.dirname(os.path.abspath(__file__))
|
||||
REPO = os.path.dirname(HIER)
|
||||
AKTEN = os.path.join(REPO, "docs", "transaktionen")
|
||||
STACK = os.path.join(REPO, "docs", "nas-vlan20", "stacks", "210-nautobot.yml")
|
||||
NB = os.environ.get("NAUTOBOT_URL", "http://10.40.20.30:8080")
|
||||
NB = os.environ.get("NAUTOBOT_URL", "http://10.40.20.154:8080")
|
||||
|
||||
PW_ENV = {"crs326-core": "CRS326_CORE_PASS", "crs326-branch": "CRS326_BRANCH_PASS",
|
||||
"ap1": "ROUTER_AP1_PASS", "ap2": "ROUTER_AP2_PASS"}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"""Restdaten-Ingest 2026-07-22: cudy1/cudy2, sw1-Ports, crs326-core Zielport."""
|
||||
import json, os, sys, urllib.parse, urllib.request, urllib.error
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ["NAUTOBOT_TOKEN"]
|
||||
DRY = "--apply" not in sys.argv
|
||||
log = []
|
||||
|
||||
@@ -38,7 +38,7 @@ ConfigContext und DCIM-Modell nicht auseinanderlaufen.
|
||||
"""
|
||||
import json, os, sys, urllib.error, urllib.parse, urllib.request
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
DRY = "--apply" not in sys.argv
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ PSKs werden bewusst NICHT modelliert (liegen lokal am Geraet).
|
||||
"""
|
||||
import json, os, sys, urllib.request, urllib.error, urllib.parse
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
DRY = "--apply" not in sys.argv
|
||||
log = []
|
||||
|
||||
@@ -27,7 +27,7 @@ tatsaechlichen Umzug wandert er zurueck auf Sideboard.
|
||||
"""
|
||||
import json, os, sys, urllib.error, urllib.parse, urllib.request
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
DRY = "--apply" not in sys.argv
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Quelle: ap1 live via MikroMCP + docs/teilprojekte.md.
|
||||
"""
|
||||
import json, sys, urllib.request, urllib.error
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
import os
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "") # aus docs/nas-vlan20/stacks/210-nautobot.yml (gitignored)
|
||||
DRY = "--apply" not in sys.argv
|
||||
|
||||
@@ -27,7 +27,7 @@ import pathlib
|
||||
import sys
|
||||
import urllib.request
|
||||
|
||||
NB = os.environ.get("NAUTOBOT_URL", "http://10.40.20.30:8080")
|
||||
NB = os.environ.get("NAUTOBOT_URL", "http://10.40.20.154:8080")
|
||||
DEVICE = "crs326-core"
|
||||
SECRETS = pathlib.Path.home() / ".secrets" / "credentials.env"
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Idempotent; verwendet GraphQL fuer tagged_vlans (REST liefert null).
|
||||
"""
|
||||
import json, os, sys, urllib.request, urllib.error, urllib.parse
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
DRY = "--apply" not in sys.argv
|
||||
log = []
|
||||
|
||||
@@ -14,7 +14,7 @@ Verifikation NUR per GraphQL (tagged_vlans liest sich per REST als null).
|
||||
"""
|
||||
import json, os, sys, urllib.request, urllib.error, urllib.parse
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
DRY = "--apply" not in sys.argv
|
||||
log = []
|
||||
|
||||
132
nautobot/ingest-dhcp-lease-rule.py
Normal file
132
nautobot/ingest-dhcp-lease-rule.py
Normal file
@@ -0,0 +1,132 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
ComplianceRule `dhcp-lease` anlegen (idempotent).
|
||||
|
||||
Die Luecke, die das schliesst
|
||||
-----------------------------
|
||||
`nautobot/mikrotik_routeros.j2` rendert seit dem 2026-09-21 den Abschnitt
|
||||
`/ip dhcp-server lease` (TP45 Schicht B, aus `local_config_context_data.dhcp_leases`).
|
||||
Gerendert heisst aber noch nicht bewacht: ohne ComplianceRule erzeugt der Block
|
||||
**keine** Compliance-Eintraege, und die Gesamtzahl bliebe unauffaellig. Genau die
|
||||
Falle aus CLAUDE.md — "eine gleichbleibende Compliance-Zahl nach dem Hinzufuegen
|
||||
eines Geraets ist ein BEFUND, kein Erfolg".
|
||||
|
||||
Konkret: von 54 live existierenden Reservierungen war vorher **keine einzige**
|
||||
drift-ueberwacht. Verschiebt jemand eine Adresse von Hand am Geraet, meldet
|
||||
Golden Config "compliant", weil die Sektion unter keiner Rule steht.
|
||||
|
||||
Analogie: TP26 fuer `/ip firewall filter` (25 Regeln blind), `ingest-queue-rule.py`
|
||||
fuer `/queue`, `ingest-wifi-access-list-rule.py` fuer `/interface wifi access-list`,
|
||||
`ingest-logging-rule.py` fuer `/system logging`.
|
||||
|
||||
export NAUTOBOT_TOKEN=... # aus docs/nas-vlan20/stacks/210-nautobot.yml
|
||||
python3 nautobot/ingest-dhcp-lease-rule.py # Dry-Run
|
||||
python3 nautobot/ingest-dhcp-lease-rule.py --apply
|
||||
"""
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
APPLY = "--apply" in sys.argv
|
||||
|
||||
FEATURE = "dhcp-lease"
|
||||
# ⚠️ Nautobot begrenzt die Feature-Beschreibung auf 200 Zeichen (400 sonst).
|
||||
BESCHREIBUNG = ("TP45 Schicht B: DHCP-Reservierungen (/ip dhcp-server lease). Bis 2026-09-21 "
|
||||
"unter keiner Rule -- die 54 Reservierungen waren drift-blind. Gleicher "
|
||||
"blinder Fleck wie TP26 fuer /ip firewall filter.")
|
||||
# Praefix, nicht die ganze Sektion: `/ip dhcp-server lease` faengt nur die Reservierungen.
|
||||
# Ein blankes `/ip dhcp-server` wuerde die Server-, Network- und Option-Zeilen mitfangen,
|
||||
# die eigene Rules haben (bzw. haben sollen).
|
||||
MATCH = "/ip dhcp-server lease"
|
||||
PLATTFORM = "RouterOS"
|
||||
|
||||
|
||||
def req(method, path, data=None):
|
||||
url = path if path.startswith("http") else BASE + path
|
||||
body = json.dumps(data).encode() if data is not None else None
|
||||
r = urllib.request.Request(url, data=body, method=method)
|
||||
r.add_header("Authorization", "Token " + TOKEN)
|
||||
r.add_header("Content-Type", "application/json")
|
||||
r.add_header("Accept", "application/json")
|
||||
try:
|
||||
with urllib.request.urlopen(r, timeout=60) as resp:
|
||||
raw = resp.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
except urllib.error.HTTPError as e:
|
||||
sys.exit("!! %s %s -> %s\n %s" % (method, url, e.code, e.read().decode()[:600]))
|
||||
|
||||
|
||||
def eins(pfad, **filter):
|
||||
q = urllib.parse.urlencode(filter)
|
||||
res = req("GET", "%s?%s" % (pfad, q)).get("results", [])
|
||||
if len(res) > 1:
|
||||
sys.exit("FEHLER: %s?%s liefert %d Treffer, erwartet 0 oder 1" % (pfad, q, len(res)))
|
||||
return res[0] if res else None
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
sys.exit("NAUTOBOT_TOKEN fehlt.")
|
||||
print("Modus:", "APPLY" if APPLY else "DRY-RUN (nichts wird geschrieben)")
|
||||
|
||||
plat = eins("/dcim/platforms/", name=PLATTFORM)
|
||||
if not plat:
|
||||
sys.exit("Plattform %r nicht gefunden." % PLATTFORM)
|
||||
print("Plattform : %s (%s)" % (plat["display"], plat["id"]))
|
||||
|
||||
feat = eins("/plugins/golden-config/compliance-feature/", name=FEATURE)
|
||||
if feat:
|
||||
print("Feature : existiert bereits (%s)" % feat["id"])
|
||||
else:
|
||||
print("Feature : anlegen -> %s" % FEATURE)
|
||||
if APPLY:
|
||||
feat = req("POST", "/plugins/golden-config/compliance-feature/",
|
||||
{"name": FEATURE, "slug": FEATURE, "description": BESCHREIBUNG})
|
||||
print(" angelegt (%s)" % feat["id"])
|
||||
|
||||
if not feat:
|
||||
print("\nDry-Run: ohne Feature kann die Rule nicht geprueft werden. Mit --apply erneut.")
|
||||
return
|
||||
|
||||
rule = eins("/plugins/golden-config/compliance-rule/",
|
||||
feature=feat["id"], platform=plat["id"])
|
||||
if rule:
|
||||
print("Rule : existiert bereits (%s), match_config=%r, config_ordered=%s"
|
||||
% (rule["id"], rule.get("match_config"), rule.get("config_ordered")))
|
||||
if rule.get("match_config") != MATCH:
|
||||
print(" ⚠️ match_config weicht ab -- erwartet %r" % MATCH)
|
||||
if rule.get("config_ordered"):
|
||||
print(" ⚠️ config_ordered=True -- erwartet False (s. u.)")
|
||||
return
|
||||
|
||||
nutzlast = {
|
||||
"feature": feat["id"],
|
||||
"platform": plat["id"],
|
||||
"description": BESCHREIBUNG,
|
||||
# 🔴 False, NICHT True: die Reservierungs-Sektion steht am Geraet in
|
||||
# Einfuege-Reihenfolge (`.30.150` frueh, `.30.5` spaet), nicht numerisch.
|
||||
# Bei config_ordered=True waere jede Umsortierung durch RouterOS ein
|
||||
# Falsch-Drift. Die Reihenfolge ist hier auch semantisch bedeutungslos —
|
||||
# anders als bei /ip firewall filter, wo sie ueber Gewinnen/Verlieren entscheidet.
|
||||
"config_ordered": False,
|
||||
"config_type": "cli",
|
||||
"match_config": MATCH,
|
||||
"custom_compliance": False,
|
||||
"config_remediation": False,
|
||||
}
|
||||
print("Rule : anlegen -> %s auf %s, match_config=%r, config_ordered=False"
|
||||
% (FEATURE, PLATTFORM, MATCH))
|
||||
if APPLY:
|
||||
r = req("POST", "/plugins/golden-config/compliance-rule/", nutzlast)
|
||||
print(" angelegt (%s)" % r["id"])
|
||||
else:
|
||||
print(" Nutzlast: %s" % json.dumps(nutzlast))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -32,7 +32,7 @@ Jinja-Template denselben String emittieren kann (sonst Dauer-Diff in der Complia
|
||||
"""
|
||||
import json, os, re, sys, urllib.error, urllib.parse, urllib.request
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
DRY = "--apply" not in sys.argv
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
APPLY = "--apply" in sys.argv
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
BASE = "http://10.40.20.154:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
APPLY = "--apply" in sys.argv
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user