TP44 Teil B durch: Prosa auf ✅ + Handoff (Sync live, täglicher Job)
- teilprojekte.md: TP44-Status ✅ DURCH; Teil-B-Ergebnis (Live-Sync als täglicher Nautobot-Job, 4/14, 0 Drift); Drift-Korrektur der falschen "CF_API_TOKEN kann kein VPC"-Gotcha (live: liest 14/14). - handoff-20260910: Root-Cause (Wurzel-__init__.py für walk_packages), register_jobs im __init__, has_sensitive_variables, VPC service_id-Fix, terpene-db service_id-Reconcile. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
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.
|
||||
@@ -2706,7 +2706,7 @@ Dauerläufer am dw. Cross-Projekt (teilzeitladen.xyz). Handoff:
|
||||
---
|
||||
|
||||
<a id="tp44"></a>
|
||||
### 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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user