diff --git a/docs/handoff-20260910-tp44-teilb-sync-live.md b/docs/handoff-20260910-tp44-teilb-sync-live.md new file mode 100644 index 0000000..f5323de --- /dev/null +++ b/docs/handoff-20260910-tp44-teilb-sync-live.md @@ -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. diff --git a/docs/teilprojekte.md b/docs/teilprojekte.md index 3d3ebd8..18bbceb 100644 --- a/docs/teilprojekte.md +++ b/docs/teilprojekte.md @@ -2706,7 +2706,7 @@ Dauerläufer am dw. Cross-Projekt (teilzeitladen.xyz). Handoff: --- -### TP44 — Cloudflare-Estate als Dokumentations-SSoT in Nautobot ▸ ⏳ **Etappe 1 ✅ · Etappe 2 Teil A ✅ (2026-09-08), offen nur noch der CF-Sync (Teil B)** +### 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 @@ -2723,16 +2723,30 @@ Dauerläufer am dw. Cross-Projekt (teilzeitladen.xyz). Handoff: 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 für Etappe 2:** `CloudService.cloud_networks` ist `read_only` (nur Through-Rows schreiben/ - lesen) · VPC-Service-API unter `connectivity/directory/services` (PUT, voller Body) · Repo-`CF_API_TOKEN` - darf kein Workers-VPC → dort über den `cloudflare`-MCP-Credential. +- 🔴 **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). -- **Offen (Etappe 2 Teil B, eigene Session):** CF-API→Nautobot-Sync — die Inline-Daten des Seeders durch - einen periodischen Live-Pull ersetzen (driftfrei). Umsetzung noch zu entscheiden (Python-cron empfohlen vs. - n8n); Plan `~/.claude/plans/tp44-weitermachen-mutable-glade.md`. Kein fertiger golden-config-**Bot** im Repo - als Vorlage — die `ingest-*`/`*-seed`-Skripte sind One-Shot, Teil B ist Neubau. +- ✅ **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.