Compare commits
2 Commits
128af14975
...
9e67ef10ba
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9e67ef10ba | ||
|
|
05f723556e |
32
README.md
32
README.md
@ -33,16 +33,39 @@ Each docker file contains a readme file, see below:
|
|||||||
* [PSQL Consumer](psql-app/README.md)
|
* [PSQL Consumer](psql-app/README.md)
|
||||||
|
|
||||||
|
|
||||||
|
## Greenfield deploy (recommended): deploy.sh
|
||||||
|
|
||||||
|
Deploying onto a new host? Use `deploy.sh` — it reconciles the host-specific
|
||||||
|
`.env` values (HOST_IP, router-facing IP/port, auth mode) *before* running
|
||||||
|
setup.sh, guards against the known portability traps
|
||||||
|
([docs/PORTABILITY-FINDINGS.md](docs/PORTABILITY-FINDINGS.md)), and brings the
|
||||||
|
stack up in stages:
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone <repo-url> && cd obmp-docker
|
||||||
|
git checkout <branch> # pin the deploy: note the branch AND commit
|
||||||
|
git log -1 --oneline # record what you deployed
|
||||||
|
./deploy.sh # interactive; or --wsl/--prod --scope ... --yes
|
||||||
|
```
|
||||||
|
|
||||||
|
A greenfield deploy is only reproducible if the checkout is pinned — "clone
|
||||||
|
and run" silently depends on whatever branch was checked out. Record the
|
||||||
|
branch + commit with the deployment.
|
||||||
|
|
||||||
|
Deployment types (`--scope full-stack|remote|central-store`) are described in
|
||||||
|
[docs/DEPLOYMENT-TYPES.md](docs/DEPLOYMENT-TYPES.md). Router-side BMP config:
|
||||||
|
[docs/router-bmp-config.md](docs/router-bmp-config.md).
|
||||||
|
|
||||||
## Using Docker Compose to run everything
|
## Using Docker Compose to run everything
|
||||||
|
|
||||||
> **Quick start (recommended):** copy `.env.example` to `.env`, fill it in, and
|
> **Quick start:** copy `.env.example` to `.env`, fill it in, and
|
||||||
> run `./setup.sh` — it creates the data directories, syncs Grafana
|
> run `./setup.sh` — it creates the data directories, syncs Grafana
|
||||||
> provisioning, and generates Authelia secrets. Then:
|
> provisioning, and generates Authelia secrets. Then:
|
||||||
> ```
|
> ```
|
||||||
> docker compose up -d # BMP collector core
|
> docker compose up -d # BMP collector core
|
||||||
> docker compose --profile test --profile auth up -d # full stack
|
> docker compose --profile test --profile auth up -d # full stack
|
||||||
> ```
|
> ```
|
||||||
> See [DOCS.md](DOCS.md) section 4 for details and the manual alternative below.
|
> See [docs/DOCS.md](docs/DOCS.md) section 4 for details and the manual alternative below.
|
||||||
|
|
||||||
### Install Docker Compose
|
### Install Docker Compose
|
||||||
You will need docker-compose. You can install that via [Docker Compose](https://docs.docker.com/compose/install/)
|
You will need docker-compose. You can install that via [Docker Compose](https://docs.docker.com/compose/install/)
|
||||||
@ -74,6 +97,11 @@ mkdir -p ${OBMP_DATA_ROOT}/grafana/dashboards
|
|||||||
sudo chmod -R 7777 $OBMP_DATA_ROOT
|
sudo chmod -R 7777 $OBMP_DATA_ROOT
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> **WARNING:** on a host with an *existing* Postgres data tree, a recursive
|
||||||
|
> chmod makes `psql_server.key` group/world-accessible and Postgres will
|
||||||
|
> refuse to start. Skip `postgres/` (setup.sh does this for you — see
|
||||||
|
> [docs/PORTABILITY-FINDINGS.md](docs/PORTABILITY-FINDINGS.md) finding 10).
|
||||||
|
|
||||||
> In order to init the DB tables, you must create the file ```${OBMP_DATA_ROOT}/config/init_db```. This should
|
> In order to init the DB tables, you must create the file ```${OBMP_DATA_ROOT}/config/init_db```. This should
|
||||||
> only be done once or whenever you want to completely wipe out the DB and start over.
|
> only be done once or whenever you want to completely wipe out the DB and start over.
|
||||||
|
|
||||||
|
|||||||
21
deploy.sh
21
deploy.sh
@ -314,7 +314,7 @@ fi
|
|||||||
case "$HOSTTYPE" in wsl|prod) ;; *) die "host type must be 'wsl' or 'prod' (got '$HOSTTYPE')";; esac
|
case "$HOSTTYPE" in wsl|prod) ;; *) die "host type must be 'wsl' or 'prod' (got '$HOSTTYPE')";; esac
|
||||||
|
|
||||||
# --- 2. deployment TYPE (described menu, resolved before auth/endpoints) -----
|
# --- 2. deployment TYPE (described menu, resolved before auth/endpoints) -----
|
||||||
# Three types from DEPLOYMENT-TYPES.md. Old scope names still accepted as
|
# Three types from docs/DEPLOYMENT-TYPES.md. Old scope names still accepted as
|
||||||
# aliases via --scope so existing invocations don't break.
|
# aliases via --scope so existing invocations don't break.
|
||||||
# full-stack = collocated ingest + store (the old 'feeders'/'full')
|
# full-stack = collocated ingest + store (the old 'feeders'/'full')
|
||||||
# remote = collector/forwarder only (the old 'standalone')
|
# remote = collector/forwarder only (the old 'standalone')
|
||||||
@ -527,8 +527,13 @@ type_resources() {
|
|||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
echo; hr; log "Deployment plan"; hr
|
echo; hr; log "Deployment plan"; hr
|
||||||
|
# Record what is being deployed - a greenfield deploy is only reproducible if
|
||||||
|
# the checkout is pinned (branch + commit go in the plan and the terminal log).
|
||||||
|
git_desc="$(git -C "$REPO_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
|
||||||
|
[ -n "$git_desc" ] && git_desc="$git_desc @ $(git -C "$REPO_DIR" rev-parse --short HEAD 2>/dev/null)$(git -C "$REPO_DIR" diff --quiet 2>/dev/null || echo ' (dirty)')"
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Repo dir : $REPO_DIR
|
Repo dir : $REPO_DIR
|
||||||
|
Checkout : ${git_desc:-not a git checkout}
|
||||||
Host type : $HOSTTYPE (auto-detected: $detected)
|
Host type : $HOSTTYPE (auto-detected: $detected)
|
||||||
Deployment type : $DTYPE
|
Deployment type : $DTYPE
|
||||||
$(type_desc "$DTYPE")
|
$(type_desc "$DTYPE")
|
||||||
@ -725,21 +730,25 @@ if [ "$HOSTTYPE" = "wsl" ] && [ "$DTYPE" != "central-store" ]; then
|
|||||||
WSL note: HOST_IP ${HOST_IP} is the WSL VM address - reachable from Windows
|
WSL note: HOST_IP ${HOST_IP} is the WSL VM address - reachable from Windows
|
||||||
and the compose network, but NOT from physical routers, and it changes on
|
and the compose network, but NOT from physical routers, and it changes on
|
||||||
'wsl --shutdown'. Routers must target the router-facing address
|
'wsl --shutdown'. Routers must target the router-facing address
|
||||||
(${ROUTER_IP}:${ROUTER_PORT}) via a portproxy on the Windows host. Run
|
(${ROUTER_IP}:${ROUTER_PORT}) via a portproxy on the Windows host. In an
|
||||||
(elevated PowerShell), substituting the real Windows LAN IP if you left a
|
ELEVATED PowerShell, run the repo's helper (auto-detects the current WSL IP,
|
||||||
placeholder above:
|
idempotent):
|
||||||
|
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts/wsl-portproxy.ps1 -RouterPort ${ROUTER_PORT}
|
||||||
|
|
||||||
|
or add the rules by hand:
|
||||||
|
|
||||||
netsh interface portproxy add v4tov4 listenport=${ROUTER_PORT} listenaddress=0.0.0.0 connectport=${COLLECTOR_PORT} connectaddress=${HOST_IP}
|
netsh interface portproxy add v4tov4 listenport=${ROUTER_PORT} listenaddress=0.0.0.0 connectport=${COLLECTOR_PORT} connectaddress=${HOST_IP}
|
||||||
netsh interface portproxy add v4tov4 listenport=9092 listenaddress=0.0.0.0 connectport=9092 connectaddress=${HOST_IP}
|
netsh interface portproxy add v4tov4 listenport=9092 listenaddress=0.0.0.0 connectport=9092 connectaddress=${HOST_IP}
|
||||||
New-NetFirewallRule -DisplayName "OpenBMP ${ROUTER_PORT}" -Direction Inbound -LocalPort ${ROUTER_PORT} -Protocol TCP -Action Allow
|
New-NetFirewallRule -DisplayName "OpenBMP ${ROUTER_PORT}" -Direction Inbound -LocalPort ${ROUTER_PORT} -Protocol TCP -Action Allow
|
||||||
|
|
||||||
Re-run the portproxy adds after any WSL restart. See router-bmp-config.md.
|
Re-run after any WSL restart (the WSL IP changes). See docs/router-bmp-config.md.
|
||||||
EOF
|
EOF
|
||||||
elif [ "$DTYPE" != "central-store" ]; then
|
elif [ "$DTYPE" != "central-store" ]; then
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
|
|
||||||
Native host. Ensure the firewall permits inbound TCP ${ROUTER_PORT} from the
|
Native host. Ensure the firewall permits inbound TCP ${ROUTER_PORT} from the
|
||||||
router mgmt network. Router config: see router-bmp-config.md (use
|
router mgmt network. Router config: see docs/router-bmp-config.md (use
|
||||||
${ROUTER_IP}:${ROUTER_PORT} as the bmp server host/port).
|
${ROUTER_IP}:${ROUTER_PORT} as the bmp server host/port).
|
||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
|
|||||||
67
docs/DEPLOYMENT-TYPES.md
Normal file
67
docs/DEPLOYMENT-TYPES.md
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# Deployment types
|
||||||
|
|
||||||
|
`deploy.sh` deploys one of three types (`--scope`). This is the reference for
|
||||||
|
what each includes, when to use it, and what to size it for. Old scope names
|
||||||
|
(`core`/`feeders`/`full`/`standalone`) are accepted as aliases.
|
||||||
|
|
||||||
|
## 1. full-stack
|
||||||
|
|
||||||
|
Everything on one host: collector + Kafka + Postgres/TimescaleDB + Grafana +
|
||||||
|
whois + lab feeders (`--profile test`: ExaBGP, GoBGP, traffic-gen, InfluxDB,
|
||||||
|
telegraf, rib-poller, churn/kafka-lag monitors). BMP terminates here and data
|
||||||
|
is stored here.
|
||||||
|
|
||||||
|
- **Use when:** one node monitors the whole fabric (lab, or small prod).
|
||||||
|
- **Excludes:** nothing except Authelia (separate toggle, `--auth authelia` +
|
||||||
|
`--profile auth`).
|
||||||
|
- **Resources:** prod-realistic 16 vCPU / 48-64 GB / NVMe >=250 GB;
|
||||||
|
lab-only 4 vCPU / 16 GB / SSD.
|
||||||
|
|
||||||
|
## 2. remote
|
||||||
|
|
||||||
|
Collector + local Kafka only. Forwards parsed/raw data to a **central store's
|
||||||
|
Kafka** (`--central-kafka HOST:PORT`, written to `KAFKA_FQDN`). No local
|
||||||
|
Postgres/psql-app/Grafana.
|
||||||
|
|
||||||
|
- **Use when:** scaling ingest out — run these near the routers so each node's
|
||||||
|
Kafka spool absorbs only its own routers' RIB dumps instead of every table
|
||||||
|
hitting one host at once.
|
||||||
|
- **Resources:** light — 2-4 vCPU / 4-8 GB / 20-40 GB fast disk (the Kafka
|
||||||
|
spool must buffer a full connect/flap burst from its local routers).
|
||||||
|
|
||||||
|
## 3. central-store
|
||||||
|
|
||||||
|
The store half: Kafka + Postgres + Grafana + feeders, **no local collector**.
|
||||||
|
Ingest arrives from remote collectors over Kafka. Run one of these behind N
|
||||||
|
`remote` nodes.
|
||||||
|
|
||||||
|
- **Use when:** you've split ingest with remote collectors.
|
||||||
|
- **Resources:** same as the full-stack store tier — 16 vCPU / 48-64 GB /
|
||||||
|
NVMe >=250 GB (it carries all remotes' data).
|
||||||
|
|
||||||
|
## Sizing: dimension for BMP burst, not steady state
|
||||||
|
|
||||||
|
BMP is event-driven and bursty. Steady state is trivial; the sizing events are
|
||||||
|
(1) the initial RIB dump on PEER_UP, (2) all routers reconnecting at once
|
||||||
|
(collector restart / RR failover), (3) reconvergence churn.
|
||||||
|
|
||||||
|
The multiplier that actually bites is **full table x monitored sessions**. A
|
||||||
|
full v4+v6 table is ~1.18M paths; in an RR topology the same table is
|
||||||
|
reflected to every client, so BMP ingests it once **per monitored session**.
|
||||||
|
Monitoring the RR-clients group means absorbing full-table x (RRs x clients)
|
||||||
|
simultaneously — that is what saturates a single host. The wall is Postgres
|
||||||
|
write latency + write amplification backpressuring psql-app -> Kafka -> the
|
||||||
|
BMP TCP sessions; the collector process itself is light and is *not* the
|
||||||
|
sizing driver.
|
||||||
|
|
||||||
|
Mitigations: NVMe with real IOPS headroom (>=5000); BMP-monitor pre-policy on
|
||||||
|
the RRs only (not every client session); stagger connects
|
||||||
|
(`initial-refresh delay/spread` on the routers); raise `PSQL_MEM_LIMIT` /
|
||||||
|
Kafka memory; or split ingest with `remote` collectors.
|
||||||
|
|
||||||
|
Storage scales with prefixes: ~1 GB per peer with a full internet table
|
||||||
|
(+~50 MB/day of timeseries); internal peers far less.
|
||||||
|
|
||||||
|
> All figures are engineering estimates to calibrate against your own
|
||||||
|
> watermarking, not measured specs. Replace them with real numbers once a
|
||||||
|
> prod-realistic node has been load-tested (see docs/production-sizing.md).
|
||||||
@ -584,7 +584,7 @@ Four advanced dashboards that go beyond basic BMP monitoring, unlocking TE/SR da
|
|||||||
|
|
||||||
### Database Schema Reference
|
### Database Schema Reference
|
||||||
|
|
||||||
A standalone database schema reference is also available at `DB_SCHEMA.md` in the repo root. It documents all 33 tables, 11 views, TE/SR columns, enum types, and common query patterns.
|
A standalone database schema reference is also available at `docs/DB_SCHEMA.md`. It documents all 33 tables, 11 views, TE/SR columns, enum types, and common query patterns.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
99
docs/PORTABILITY-FINDINGS.md
Normal file
99
docs/PORTABILITY-FINDINGS.md
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Portability findings — what breaks on a greenfield deploy
|
||||||
|
|
||||||
|
Every artifact hit while standing this stack up on a fresh host (WSL2
|
||||||
|
`NWE-LT02`, Ubuntu 24.04, native docker-ce 29.x — 2026-07), ordered roughly as
|
||||||
|
encountered. Each one is something a clean deploy has to survive; most are now
|
||||||
|
handled automatically by `deploy.sh`/`setup.sh`. Kept as a checklist for the
|
||||||
|
next new host and as the rationale for the deploy tooling.
|
||||||
|
|
||||||
|
## 1. Bind-mounted volumes need pre-existing source dirs
|
||||||
|
`docker-compose.yml` binds `${OBMP_DATA_ROOT}/postgres/{data,ts}` with
|
||||||
|
`type:none,o:bind`. Docker will not create these; if absent, Postgres fails
|
||||||
|
with `no such file or directory`. `setup.sh` creates them — the error only
|
||||||
|
appears when setup.sh wasn't run on the target. deploy.sh double-checks the
|
||||||
|
dirs exist after setup.sh.
|
||||||
|
|
||||||
|
## 2. Stale HOST_IP survives a copied .env
|
||||||
|
A `.env` copied from another host carries that host's `HOST_IP`; setup.sh
|
||||||
|
validation only checks non-empty/not-"changeme", so the stale IP passes and
|
||||||
|
gets rendered into `gobgpd.conf` and the Grafana root URL. **This is the core
|
||||||
|
reason deploy.sh exists** — it reconciles HOST_IP (and warns when the .env
|
||||||
|
value isn't a local address) *before* setup.sh renders anything.
|
||||||
|
|
||||||
|
## 3. A populated .env is not a deployed host
|
||||||
|
The same copied .env had late-stage artifacts (Authelia secrets) while
|
||||||
|
early-stage host state (Postgres dirs) was missing — proof that setup.sh
|
||||||
|
completed on a *different* host. Run setup.sh per target, always.
|
||||||
|
|
||||||
|
## 4. WSL: HOST_IP is the NAT address; routers can't reach it
|
||||||
|
`hostname -I` in WSL returns the VM's NAT address — it changes on
|
||||||
|
`wsl --shutdown` and physical routers cannot reach it. Routers must target the
|
||||||
|
**Windows LAN IP**, forwarded into WSL via `netsh portproxy`
|
||||||
|
(`scripts/wsl-portproxy.ps1`). deploy.sh separates "router-facing IP:port"
|
||||||
|
from HOST_IP and auto-detects the Windows LAN IP via powershell.exe interop.
|
||||||
|
|
||||||
|
## 5. Cold-start contention masquerades as component bugs
|
||||||
|
Bringing the whole stack up at once crash-looped Kafka on a preflight
|
||||||
|
"writable" check that was actually resource contention, not permissions.
|
||||||
|
deploy.sh stages the bring-up: infra (zookeeper/kafka) -> psql -> core ->
|
||||||
|
feeders.
|
||||||
|
|
||||||
|
## 6. Size for BMP burst, not steady state
|
||||||
|
Generic docs say the collector is light — true, and misleading: the store
|
||||||
|
saturates under burst (full table x monitored sessions, Postgres write
|
||||||
|
amplification). See docs/DEPLOYMENT-TYPES.md for the full model.
|
||||||
|
|
||||||
|
## 7. Ports 5000 and 3000 are crowded
|
||||||
|
The collector's 5000 and Grafana's 3000 are commonly already occupied.
|
||||||
|
deploy.sh runs a port-collision preflight before touching anything. The
|
||||||
|
router-facing BMP port default moved 5000 -> **1790** (the IANA BMP port);
|
||||||
|
the collector still listens on 5000 inside the container.
|
||||||
|
|
||||||
|
## 8. Docker Desktop breaks host bind mounts on WSL (the big one)
|
||||||
|
Symptom: Postgres mount fails `no such file or directory` on a path `ls`
|
||||||
|
shows exists. Cause: Docker Desktop runs the daemon in its own WSL distro
|
||||||
|
(`docker-desktop` VM), so host bind mounts resolve against *that* filesystem,
|
||||||
|
not the distro you ran compose from. No mkdir fixes it. **Fix: native
|
||||||
|
docker-in-WSL** — disable Desktop's WSL integration, install docker-ce,
|
||||||
|
`systemctl enable --now docker`, and confirm
|
||||||
|
`docker info | grep 'Operating System'` does not say Docker Desktop.
|
||||||
|
deploy.sh detects Docker Desktop and warns/refuses.
|
||||||
|
|
||||||
|
### 8b. Desktop residue splits the mount namespace
|
||||||
|
After installing native docker, the CLI couldn't reach `/run/docker.sock`
|
||||||
|
even though dockerd held it — a stale mount-namespace split left by Desktop.
|
||||||
|
`wsl --shutdown` and reopening the distro cleared it.
|
||||||
|
|
||||||
|
## 9. Grafana customizations must live in the repo, not grafana.db
|
||||||
|
Dashboards hand-built in the UI exist only in that host's `grafana.db` and
|
||||||
|
vanish on a greenfield deploy. All dashboards are now committed as JSON under
|
||||||
|
`obmp-grafana/dashboards/` and setup.sh syncs both provisioning YAML *and*
|
||||||
|
dashboard JSONs to the data root. If you edit a dashboard in the UI, export
|
||||||
|
and commit the JSON.
|
||||||
|
|
||||||
|
## 10. Blanket `chmod -R 777` on the data root breaks Postgres re-deploys
|
||||||
|
setup.sh's lab-permissive chmod over an *existing* data tree made
|
||||||
|
`psql_server.key` world-accessible; Postgres refuses to boot
|
||||||
|
(`FATAL: private key file ... has group or world access`) and the whole core
|
||||||
|
cascades down. Fresh installs never hit it (the key is generated after the
|
||||||
|
chmod) — it is strictly a re-deploy bug. setup.sh now skips `postgres/`
|
||||||
|
(the image entrypoint owns those perms) and re-tightens the key/cert to 0600
|
||||||
|
if a previous run already clobbered them.
|
||||||
|
|
||||||
|
## 11. docker-ce 29 rejects Docker API < 1.40 — old pinned clients break
|
||||||
|
telegraf 1.28's `[[inputs.docker]]` hardcodes Docker API version 1.24, which
|
||||||
|
docker-ce >= 29 refuses (`client version 1.24 is too old`). Every
|
||||||
|
per-container stack metric silently never reached InfluxDB; the stack
|
||||||
|
dashboards sat empty while `disk`/`postgresql_*` inputs worked fine.
|
||||||
|
`DOCKER_API_VERSION` env is ignored (version pinned in code) — the fix is
|
||||||
|
telegraf >= 1.30, which negotiates the API version (telegraf/Dockerfile now
|
||||||
|
pins 1.33). Watch for the same failure in any other tooling that talks to the
|
||||||
|
daemon with an old vendored client.
|
||||||
|
|
||||||
|
## 12. OPEN: router-side automation still targets the old lab's values
|
||||||
|
`cml/proxmox_bmp_config.py` sends routers at `HOST_IP` port `5000`, and
|
||||||
|
`cml/xrd-node-definition.yaml` bakes in `10.40.40.202:5000` — both correct on
|
||||||
|
the old native-Linux dev lab, wrong on a WSL host (finding 4: routers must
|
||||||
|
target the router-facing address, and the port default is now 1790). Fold
|
||||||
|
`ROUTER_FACING_IP`/`ROUTER_FACING_PORT` from `.env` into that tooling when
|
||||||
|
the 5000-vs-1790 decision lands. See docs/router-bmp-config.md.
|
||||||
95
docs/handoff/00-HANDOFF-BRIEF.md
Normal file
95
docs/handoff/00-HANDOFF-BRIEF.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Handoff brief — OpenBMP greenfield deployment work
|
||||||
|
|
||||||
|
**From:** Claude (chat instance) working with Sam on deployment/portability.
|
||||||
|
**To:** Claude (VSCode instance) working directly in the `obmp-docker` Gitea repo.
|
||||||
|
**Date:** 2026-07-20.
|
||||||
|
|
||||||
|
We don't share memory. This packet is the bridge. Sam is the channel between us.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## One-paragraph summary
|
||||||
|
|
||||||
|
Sam is standing the `obmp-docker` stack up on a **new WSL2 host** (`NWE-LT02`,
|
||||||
|
Ubuntu 24.04), separate from the dev lab, to shake out greenfield-portability
|
||||||
|
problems before prod. Over this session we built a `deploy.sh` wrapper around
|
||||||
|
the repo's existing `setup.sh` that reconciles host-specific `.env` values
|
||||||
|
*before* provisioning, plus supporting docs. We hit and fixed a long chain of
|
||||||
|
deployment artifacts (details in `02-FINDINGS.md`). The stack is now **finally
|
||||||
|
pulling images and coming up on a native docker daemon** after the big
|
||||||
|
blocker — Docker Desktop's cross-VM bind-mount failure — was resolved by
|
||||||
|
switching to native docker-in-WSL. **Current open thread: Grafana came up
|
||||||
|
without its expected customizations, and we're not sure the repo checkout is on
|
||||||
|
the right branch.** That's where you come in — you can see the repo directly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What this session produced (lives in Sam's chat outputs, not the repo yet)
|
||||||
|
|
||||||
|
A `deploy.sh` + docs bundle intended to drop into the repo root. Sam has these
|
||||||
|
files; they are NOT yet committed to the repo (that's a decision for you + Sam):
|
||||||
|
|
||||||
|
- `deploy.sh` — interactive greenfield deploy wrapper (see `01-DEPLOY-SCRIPT.md`)
|
||||||
|
- `DEPLOYMENT-TYPES.md` — the three deployment types, locked
|
||||||
|
- `PORTABILITY-FINDINGS.md` — everything that broke and why
|
||||||
|
- `router-bmp-config.md` — IOS-XR BMP config reference
|
||||||
|
- `README.md` — bundle overview
|
||||||
|
|
||||||
|
If you want to integrate these into the repo, coordinate branch/placement with
|
||||||
|
Sam. They were written against the repo's current `setup.sh`, `.env.example`,
|
||||||
|
and `docker-compose.yml` as pasted into chat.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## THE OPEN QUESTION FOR YOU (highest priority)
|
||||||
|
|
||||||
|
Grafana is up but **its customizations are missing**. Sam expected them to be
|
||||||
|
"part of the repo." Then Sam raised the real doubt: **"did I even pull the
|
||||||
|
correct branch?"**
|
||||||
|
|
||||||
|
You can answer this directly — you have the repo. Please determine:
|
||||||
|
|
||||||
|
1. **What branch/commit is the working checkout on?**
|
||||||
|
`git branch --show-current && git log --oneline -5 && git status`
|
||||||
|
|
||||||
|
2. **Is the Grafana provisioning committed, and on which branch?**
|
||||||
|
The stack provisions Grafana from `obmp-grafana/provisioning/` (setup.sh
|
||||||
|
copies it to `${OBMP_DATA_ROOT}/grafana/provisioning/`, bind-mounted into
|
||||||
|
the container). Check whether dashboards/datasources actually exist in the
|
||||||
|
tree, and on which branch:
|
||||||
|
```
|
||||||
|
git ls-tree -r --name-only HEAD -- obmp-grafana/provisioning/
|
||||||
|
git branch -a
|
||||||
|
git ls-tree -r --name-only origin/main -- obmp-grafana/provisioning/
|
||||||
|
# and any feature branch you've been working on
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Were the Grafana customizations ever committed as code**, or did they only
|
||||||
|
ever exist as UI state in a `grafana.db` (i.e. hand-built, never exported to
|
||||||
|
provisioning)? This is the crux: "supposed to be in the repo" may have been
|
||||||
|
intent, not reality. You'd know — did you commit dashboard JSON?
|
||||||
|
|
||||||
|
The fork:
|
||||||
|
- **On wrong branch** → check out the right one, redeploy; provisioning returns.
|
||||||
|
- **Right branch, provisioning present but not loading** → permissions/timing;
|
||||||
|
`docker compose restart grafana` after confirming files are in
|
||||||
|
`/var/openbmp/grafana/provisioning/`.
|
||||||
|
- **Provisioning never committed** → the customizations were UI-only; they need
|
||||||
|
exporting to the repo now so future greenfield deploys reproduce them. This is
|
||||||
|
itself a portability finding.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Immediate deployment state (as of handoff)
|
||||||
|
|
||||||
|
- Host: WSL2, Ubuntu 24.04.4, `NWE-LT02`, 22 vCPU / ~15 GB RAM.
|
||||||
|
- Daemon: **native docker-ce 29.6.2** (NOT Docker Desktop — that was the bug).
|
||||||
|
`docker info` confirms `Operating System: Ubuntu 24.04.4 LTS`.
|
||||||
|
- `OBMP_DATA_ROOT=/var/openbmp` (native ext4, `/dev/sdg`).
|
||||||
|
- Deploy command in use: `./deploy.sh --wsl --scope full-stack --auth local`
|
||||||
|
- Stack was pulling images and progressing past the volume-mount failure that
|
||||||
|
blocked every earlier attempt.
|
||||||
|
- Grafana reachable at `http://<wsl-ip>:3000/grafana/`, login `admin`/`openbmp`.
|
||||||
|
|
||||||
|
See `02-FINDINGS.md` for the full list of what broke and how it was fixed, and
|
||||||
|
`03-NEXT-STEPS.md` for what remains.
|
||||||
55
docs/handoff/01-DEPLOY-SCRIPT.md
Normal file
55
docs/handoff/01-DEPLOY-SCRIPT.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# deploy.sh — what it is and how it behaves
|
||||||
|
|
||||||
|
An interactive wrapper around the repo's `setup.sh`. Its job: reconcile
|
||||||
|
**host-specific** values in `.env` for THIS host *before* `setup.sh` renders
|
||||||
|
config from them, then bring the stack up in stages. It exists because copying
|
||||||
|
a populated `.env` between hosts silently propagates stale values (see
|
||||||
|
`02-FINDINGS.md`, finding 2/3).
|
||||||
|
|
||||||
|
## Flow (what happens on a run)
|
||||||
|
|
||||||
|
1. Detect host type (WSL vs native Linux); let user override.
|
||||||
|
2. Guard: refuse/warn if the active daemon is **Docker Desktop** (bind mounts
|
||||||
|
break under it — see findings).
|
||||||
|
3. Resolve deployment **type** (menu with descriptions + resource estimates).
|
||||||
|
4. Resolve `HOST_IP` (internal bind), **router-facing IP+port** (separate — what
|
||||||
|
`bmp server` targets), auth mode, and for `remote` type the central Kafka.
|
||||||
|
5. On WSL: auto-detect the **Windows LAN IP** via `powershell.exe` interop for
|
||||||
|
the router-facing default (with graceful fallback to a placeholder).
|
||||||
|
6. **Port-collision preflight** — checks host ports the type will bind
|
||||||
|
(ss/netstat//proc), warns/refuses on conflict.
|
||||||
|
7. Show a plan; one confirmation.
|
||||||
|
8. Write reconciled values to `.env`.
|
||||||
|
9. Clear stale volume handles → run `setup.sh` → verify bind dirs exist →
|
||||||
|
clear handles again → **staged bring-up** → verify.
|
||||||
|
10. Print post-deploy notes (WSL: netsh portproxy commands; native: firewall +
|
||||||
|
router `bmp server` snippet).
|
||||||
|
|
||||||
|
## Deployment types (also in DEPLOYMENT-TYPES.md)
|
||||||
|
|
||||||
|
- `full-stack` — collector + Kafka + Postgres + Grafana + feeders, one host.
|
||||||
|
- `remote` — collector + local Kafka only, forwards to a central store's Kafka.
|
||||||
|
Needs `--central-kafka HOST:PORT`. No local store.
|
||||||
|
- `central-store` — store + Grafana + feeders, NO local collector; ingest via
|
||||||
|
remotes.
|
||||||
|
|
||||||
|
Old scope names (`core`/`feeders`/`full`/`standalone`) are accepted as aliases.
|
||||||
|
|
||||||
|
## Flags
|
||||||
|
|
||||||
|
`--wsl|--prod`, `--host-ip`, `--router-ip`, `--router-port` (default **1790**,
|
||||||
|
the IANA BMP port — collector still listens 5000 internally, mapped),
|
||||||
|
`--auth local|authelia`, `--scope full-stack|remote|central-store`,
|
||||||
|
`--central-kafka HOST:PORT`, `--reset`, `--yes`.
|
||||||
|
|
||||||
|
## Notes for repo integration
|
||||||
|
|
||||||
|
- It calls `./setup.sh` and checks its exit status. It does NOT reimplement
|
||||||
|
setup.sh's provisioning — it drives it with correct inputs.
|
||||||
|
- It assumes `.env.example`, `setup.sh`, `docker-compose.yml` are in the same
|
||||||
|
dir. If `setup.sh` key names change, update deploy.sh's `get_env`/`set_env`
|
||||||
|
references.
|
||||||
|
- Router port default was changed to 1790; the lab routers are currently
|
||||||
|
configured to send BMP to **5000**, so until they're reconfigured, deploy
|
||||||
|
with `--router-port 5000` OR update the routers. This is an open decision
|
||||||
|
(see 03-NEXT-STEPS.md).
|
||||||
74
docs/handoff/02-FINDINGS.md
Normal file
74
docs/handoff/02-FINDINGS.md
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
# Deployment findings — what broke on greenfield and why
|
||||||
|
|
||||||
|
Ordered roughly as encountered. Each is a real portability artifact that a
|
||||||
|
clean deploy has to survive. These are captured in full in the bundle's
|
||||||
|
`PORTABILITY-FINDINGS.md`; this is the digest for you.
|
||||||
|
|
||||||
|
## 1. Bind-mounted volumes need pre-existing source dirs
|
||||||
|
`docker-compose.yml` binds `${OBMP_DATA_ROOT}/postgres/{data,ts}` with
|
||||||
|
`type:none,o:bind`. Docker won't create these; if absent, Postgres fails with
|
||||||
|
`no such file or directory`. `setup.sh` DOES create them (its data-tree loop) —
|
||||||
|
the error only appears when setup.sh wasn't run on the target.
|
||||||
|
|
||||||
|
## 2. Stale HOST_IP survives a copied .env, and setup.sh doesn't catch it
|
||||||
|
`.env` carried `HOST_IP=10.13.21.65` from another host — not an address on the
|
||||||
|
new box. setup.sh validation only checks non-empty/not-"changeme", so a stale
|
||||||
|
IP passes and gets rendered into `gobgpd.conf` + Grafana URL. deploy.sh fixes
|
||||||
|
HOST_IP before setup.sh runs. **This is the core reason deploy.sh exists.**
|
||||||
|
|
||||||
|
## 3. .env travels, host state doesn't
|
||||||
|
Authelia secrets were populated in the copied `.env` (late setup.sh step) but
|
||||||
|
the Postgres dirs (early step) were missing — proving setup.sh completed on a
|
||||||
|
*different* host and the .env was copied over without re-running setup.sh here.
|
||||||
|
Lesson: a populated .env is not a deployed host; run setup.sh per target.
|
||||||
|
|
||||||
|
## 4. WSL: HOST_IP is the NAT addr; not router-reachable
|
||||||
|
`hostname -I` on WSL returns the VM's NAT address — changes on `wsl --shutdown`,
|
||||||
|
not reachable by physical routers. Routers must target the **Windows LAN IP**,
|
||||||
|
forwarded via `netsh portproxy` into WSL. deploy.sh now auto-detects the Windows
|
||||||
|
LAN IP via `powershell.exe` interop (read-only, no admin needed) and separates
|
||||||
|
"router-facing IP" from "HOST_IP".
|
||||||
|
|
||||||
|
## 5. Cold-start contention looked like a Kafka permission bug
|
||||||
|
Bringing the whole stack up at once crash-looped Kafka on a preflight
|
||||||
|
"writable" check that was actually resource contention, not permissions.
|
||||||
|
deploy.sh stages the bring-up (infra → core → feeders).
|
||||||
|
|
||||||
|
## 6. Sizing: dimension for BMP burst, not steady state
|
||||||
|
Generic docs say the collector is light. True for steady state, misleading for
|
||||||
|
sizing: the store (Postgres) saturates under burst. Sam's lab is GoBGP pulling
|
||||||
|
full v4/v6 (~1.18M paths) reflected through RRs to clients — load is
|
||||||
|
full-table × monitored-sessions, and Postgres write amplification is the wall.
|
||||||
|
Numbers in DEPLOYMENT-TYPES.md are engineering estimates to calibrate against
|
||||||
|
Sam's watermarking, explicitly not measured specs.
|
||||||
|
|
||||||
|
## 7. Port 5000/3000 collisions
|
||||||
|
5000 (OpenBMP collector) and 3000 (Grafana) are commonly occupied. deploy.sh
|
||||||
|
now runs a port-collision preflight before bring-up. Also: router-facing BMP
|
||||||
|
port default moved 5000 → **1790** (IANA standard) to avoid the crowded 5000;
|
||||||
|
collector still listens 5000 internally, external maps down.
|
||||||
|
|
||||||
|
## 8. **Docker Desktop breaks host bind mounts on WSL** (the big one)
|
||||||
|
Symptom: Postgres mount fails `no such file or directory` on a path that `ls`
|
||||||
|
shows exists. Cause: Docker Desktop runs the daemon in its OWN wsl distro
|
||||||
|
(`docker-desktop` VM); host bind mounts resolve against THAT filesystem, not
|
||||||
|
the working distro. Two different filesystems. No `mkdir` in the working distro
|
||||||
|
fixes it. **Fix: switch to native docker-in-WSL** (disable Desktop WSL
|
||||||
|
integration, install docker-ce, `systemctl enable --now docker`). Confirm with
|
||||||
|
`docker info | grep 'Operating System'` — must NOT say Docker Desktop. This also
|
||||||
|
makes the WSL box behave like the native-Linux prod host. deploy.sh now detects
|
||||||
|
Docker Desktop and warns/refuses. Resolving this is what finally let the stack
|
||||||
|
pull images and progress.
|
||||||
|
|
||||||
|
## 8b. Native docker socket namespace snag (post-switch)
|
||||||
|
After installing native docker, the CLI couldn't reach the socket
|
||||||
|
(`/run/docker.sock` listened-on by dockerd but not visible to the shell — mount
|
||||||
|
namespace split left by Desktop residue). Fixed by `wsl --shutdown` and
|
||||||
|
reopening, which cleared the stale namespaces. After restart `docker info`
|
||||||
|
correctly reported Ubuntu.
|
||||||
|
|
||||||
|
## 9. OPEN: Grafana customizations missing / branch uncertainty
|
||||||
|
Grafana came up without expected customizations. Provisioning is supposed to be
|
||||||
|
repo-committed (`obmp-grafana/provisioning/`). Sam then questioned whether the
|
||||||
|
correct branch was even checked out. **This is the handoff question for the
|
||||||
|
VSCode instance** — see 00-HANDOFF-BRIEF.md.
|
||||||
66
docs/handoff/03-NEXT-STEPS.md
Normal file
66
docs/handoff/03-NEXT-STEPS.md
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# Next steps & open decisions
|
||||||
|
|
||||||
|
## Immediate (blocking the current deploy)
|
||||||
|
|
||||||
|
1. **Resolve the branch/provisioning question** (VSCode instance — you own this):
|
||||||
|
- Confirm which branch the checkout is on and whether it's the intended one.
|
||||||
|
- Confirm whether `obmp-grafana/provisioning/` (dashboards + datasources) is
|
||||||
|
committed, and on which branch.
|
||||||
|
- If provisioning exists but Grafana didn't load it: verify files reached
|
||||||
|
`/var/openbmp/grafana/provisioning/`, check ownership (Grafana UID 472),
|
||||||
|
`docker compose restart grafana`, check logs for provisioning errors.
|
||||||
|
- If provisioning was never committed (UI-only): export current/expected
|
||||||
|
dashboards to JSON and commit them so greenfield deploys reproduce them.
|
||||||
|
This closes a real portability gap.
|
||||||
|
|
||||||
|
2. **Pin the deploy to a known branch/commit.** A greenfield deploy is only
|
||||||
|
reproducible if the checkout is pinned. Recommend documenting "clone, check
|
||||||
|
out <branch> at <commit>, run deploy.sh" — otherwise "clone and run" silently
|
||||||
|
depends on whatever branch was checked out. Consider adding a branch/commit
|
||||||
|
check to deploy.sh or the README.
|
||||||
|
|
||||||
|
## Deployment decisions still open
|
||||||
|
|
||||||
|
3. **Router BMP port: 5000 vs 1790.** deploy.sh now defaults router-facing to
|
||||||
|
1790 (IANA standard, avoids crowded 5000). BUT the lab routers are currently
|
||||||
|
configured to send to **5000**. Decision: either
|
||||||
|
- reconfigure the routers to 1790 (Sam leaned this way — cleaner, avoids
|
||||||
|
common-port collisions), which needs the IOS-XR `bmp server` port change
|
||||||
|
on each device and a compose mapping `1790:5000` on native hosts; or
|
||||||
|
- keep 5000 and deploy with `--router-port 5000`.
|
||||||
|
If moving to 1790: router-bmp-config.md needs the port updated, and the
|
||||||
|
native-host compose `ports:` for the collector needs `1790:5000`.
|
||||||
|
|
||||||
|
4. **Sizing profiles.** DEPLOYMENT-TYPES.md has three types with ballpark
|
||||||
|
resources (estimates, not measured). Sam is watermarking a prod-realistic
|
||||||
|
node to replace estimates with real numbers. When those land, update the
|
||||||
|
figures. There was an idea to add a `--sizing lab|prod-lite|prod` selector
|
||||||
|
that writes `*_MEM_LIMIT` blocks into `.env`; deferred until watermarks exist.
|
||||||
|
|
||||||
|
5. **Doc name sync.** deploy.sh was renamed to the three deployment types after
|
||||||
|
the docs were written; a couple of README/doc tables may still reference the
|
||||||
|
old scope names (feeders/full/standalone). Cosmetic — aliases keep it
|
||||||
|
working — but worth a sync pass.
|
||||||
|
|
||||||
|
## Integration decisions
|
||||||
|
|
||||||
|
6. **Where do the bundle files live?** deploy.sh + the 4 docs currently exist
|
||||||
|
only in Sam's chat outputs. Decide whether they get committed to the repo
|
||||||
|
(and to which branch), and whether deploy.sh supersedes or complements any
|
||||||
|
existing deploy tooling in the repo you may know about that I don't.
|
||||||
|
|
||||||
|
7. **Reconcile with whatever you (VSCode) have been building.** I have no
|
||||||
|
visibility into your repo work — the EVPN consumer, gobgp config, any CI, or
|
||||||
|
other tooling. If there's overlap or conflict between deploy.sh and existing
|
||||||
|
repo scripts, that's a reconciliation for you + Sam. Flag anything I've
|
||||||
|
duplicated or contradicted.
|
||||||
|
|
||||||
|
## Things that are DONE (don't redo)
|
||||||
|
|
||||||
|
- Native docker-in-WSL is installed and working; Docker Desktop is the wrong
|
||||||
|
substrate (finding 8). Don't re-recommend Desktop.
|
||||||
|
- The `.env` HOST_IP/auth reconciliation, staged bring-up, port preflight,
|
||||||
|
Windows-LAN-IP detection, and Docker Desktop guard are all built and tested
|
||||||
|
in deploy.sh.
|
||||||
|
- The bind-dir + stale-volume handling in deploy.sh is hardened (down+rm before
|
||||||
|
AND after setup.sh, plus a dir-existence check).
|
||||||
10
docs/handoff/README.md
Normal file
10
docs/handoff/README.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Handoff packets
|
||||||
|
|
||||||
|
Point-in-time bridge documents written by one Claude session for another
|
||||||
|
during the 2026-07 greenfield portability push (chat instance -> repo/VSCode
|
||||||
|
instance, with Sam as the channel). Kept for the record; **the live versions
|
||||||
|
of their content are the repo docs** — findings in
|
||||||
|
[../PORTABILITY-FINDINGS.md](../PORTABILITY-FINDINGS.md), deployment types in
|
||||||
|
[../DEPLOYMENT-TYPES.md](../DEPLOYMENT-TYPES.md), router config in
|
||||||
|
[../router-bmp-config.md](../router-bmp-config.md). Where these packets and
|
||||||
|
the repo disagree, the repo is right.
|
||||||
98
docs/router-bmp-config.md
Normal file
98
docs/router-bmp-config.md
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
# Router BMP configuration (IOS-XR)
|
||||||
|
|
||||||
|
How to point routers at the OpenBMP collector, and which address/port they
|
||||||
|
must target depending on where the stack runs. Companion automation:
|
||||||
|
`cml/proxmox_bmp_config.py` (applies the block below over SSH to the Proxmox
|
||||||
|
lab routers) and `cml/xrd-node-definition.yaml` (bakes it into XRd images).
|
||||||
|
|
||||||
|
## What address do routers target?
|
||||||
|
|
||||||
|
**Never `HOST_IP` on a WSL deployment.** `HOST_IP` is the WSL VM's NAT
|
||||||
|
address — unreachable from physical routers and changed by `wsl --shutdown`.
|
||||||
|
deploy.sh records the correct target in `.env` as
|
||||||
|
`ROUTER_FACING_IP` / `ROUTER_FACING_PORT`:
|
||||||
|
|
||||||
|
| Stack host | Routers target | Path to the collector |
|
||||||
|
|---|---|---|
|
||||||
|
| Native Linux | `ROUTER_FACING_IP` (= host IP) : `ROUTER_FACING_PORT` | compose port map -> collector :5000 |
|
||||||
|
| WSL2 | Windows LAN IP : `ROUTER_FACING_PORT` | `netsh portproxy` (Windows) -> WSL `HOST_IP` -> collector :5000 |
|
||||||
|
|
||||||
|
On WSL the Windows-side forwarding must exist first — run
|
||||||
|
`scripts/wsl-portproxy.ps1` in an **elevated** PowerShell, and re-run it after
|
||||||
|
every WSL restart (the WSL address changes).
|
||||||
|
|
||||||
|
The router-facing port defaults to **1790** (the IANA-registered BMP port);
|
||||||
|
the collector always listens on **5000 inside the container**. The lab
|
||||||
|
routers were historically configured for 5000 — until they are reconfigured,
|
||||||
|
deploy with `--router-port 5000` (see the open decision in
|
||||||
|
docs/PORTABILITY-FINDINGS.md, finding 12).
|
||||||
|
|
||||||
|
## `bmp server` block
|
||||||
|
|
||||||
|
Flat formal form (submode-free, safe to paste line-by-line at `(config)#` —
|
||||||
|
the form `proxmox_bmp_config.py` applies):
|
||||||
|
|
||||||
|
```
|
||||||
|
bmp server 1 host <ROUTER_FACING_IP> port <ROUTER_FACING_PORT>
|
||||||
|
bmp server 1 description OpenBMP-Collector
|
||||||
|
bmp server 1 update-source MgmtEth0/RP0/CPU0/0
|
||||||
|
bmp server 1 initial-delay 60
|
||||||
|
bmp server 1 stats-reporting-period 300
|
||||||
|
bmp server 1 initial-refresh delay 60 spread 30
|
||||||
|
```
|
||||||
|
|
||||||
|
The `initial-refresh delay/spread` staggering matters at scale: it spreads
|
||||||
|
the full-RIB dumps out when many routers (re)connect at once, which is the
|
||||||
|
burst that sizes the whole store tier (docs/DEPLOYMENT-TYPES.md).
|
||||||
|
|
||||||
|
## Activating monitoring on BGP sessions
|
||||||
|
|
||||||
|
BMP only reports sessions that are `bmp-activate`d:
|
||||||
|
|
||||||
|
```
|
||||||
|
router bgp <ASN>
|
||||||
|
neighbor-group RR-CLIENTS
|
||||||
|
bmp-activate server 1
|
||||||
|
!
|
||||||
|
!
|
||||||
|
```
|
||||||
|
|
||||||
|
**Monitor on the RRs, pre-policy, not on every client session.** In an RR
|
||||||
|
topology the same full table is reflected to every client; activating BMP on
|
||||||
|
all reflected sessions makes the collector ingest full-table x sessions
|
||||||
|
copies simultaneously. Activating on the RR side only is the single biggest
|
||||||
|
load reduction available.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Router side:
|
||||||
|
|
||||||
|
```
|
||||||
|
show bgp bmp server 1 ! state should be ESTAB, uptime climbing
|
||||||
|
show bgp bmp summary
|
||||||
|
```
|
||||||
|
|
||||||
|
Collector side:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker logs obmp-collector --tail 20 # look for ROUTER_INIT / PEER_UP
|
||||||
|
docker exec -i obmp-psql psql -U openbmp -d openbmp \
|
||||||
|
-c "SELECT name, ip_address, bgp_id, isconnected FROM routers ORDER BY name;"
|
||||||
|
```
|
||||||
|
|
||||||
|
Grafana: the OBMP-Operations / Inventory and Peer Detail dashboards populate
|
||||||
|
within a minute of the first PEER_UP; RIB contents follow as the initial dump
|
||||||
|
is consumed (large tables take longer — watch the OBMP-Telemetry / Kafka Lag
|
||||||
|
dashboard drain).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **Session cycles ESTAB -> idle:** the collector accepted TCP but the
|
||||||
|
pipeline is backpressured (Postgres write latency) — check the stack
|
||||||
|
dashboards, not the router.
|
||||||
|
- **No TCP at all from a WSL deployment:** portproxy rule missing/stale
|
||||||
|
(re-run `scripts/wsl-portproxy.ps1`), or Windows firewall lacks the inbound
|
||||||
|
allow for the router-facing port.
|
||||||
|
- **Router config rejected:** IOS-XR BMP config is not in the NETCONF YANG
|
||||||
|
schema on the lab release — apply via SSH CLI (which is why
|
||||||
|
`proxmox_bmp_config.py` drives the CLI).
|
||||||
67
scripts/wsl-portproxy.ps1
Normal file
67
scripts/wsl-portproxy.ps1
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
Forward router-facing OpenBMP ports from the Windows host into WSL.
|
||||||
|
|
||||||
|
.DESCRIPTION
|
||||||
|
Physical routers cannot reach the WSL VM's NAT address, and that address
|
||||||
|
changes on every 'wsl --shutdown' (portability finding 4). This script
|
||||||
|
(re)creates the netsh portproxy rules that forward the router-facing BMP
|
||||||
|
port and Kafka from the Windows LAN IP into the current WSL address, plus
|
||||||
|
the matching inbound firewall rules. Idempotent: existing rules for the
|
||||||
|
same listen ports are replaced, so RE-RUN IT AFTER EVERY WSL RESTART.
|
||||||
|
|
||||||
|
Must run in an ELEVATED PowerShell.
|
||||||
|
|
||||||
|
.PARAMETER RouterPort
|
||||||
|
Router-facing BMP listen port on Windows (default 1790, the IANA BMP port).
|
||||||
|
Forwards to the collector's in-container port 5000.
|
||||||
|
|
||||||
|
.PARAMETER WslIp
|
||||||
|
WSL address to forward to. Default: auto-detected via 'wsl hostname -I'.
|
||||||
|
|
||||||
|
.EXAMPLE
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts\wsl-portproxy.ps1
|
||||||
|
powershell -ExecutionPolicy Bypass -File scripts\wsl-portproxy.ps1 -RouterPort 5000
|
||||||
|
#>
|
||||||
|
param(
|
||||||
|
[int]$RouterPort = 1790,
|
||||||
|
[int]$CollectorPort = 5000,
|
||||||
|
[int]$KafkaPort = 9092,
|
||||||
|
[string]$WslIp = ""
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$id = [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
||||||
|
if (-not (New-Object System.Security.Principal.WindowsPrincipal($id)).IsInRole(
|
||||||
|
[System.Security.Principal.WindowsBuiltInRole]::Administrator)) {
|
||||||
|
Write-Error "Run this from an ELEVATED PowerShell (netsh portproxy needs admin)."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $WslIp) {
|
||||||
|
$WslIp = (wsl hostname -I).Trim().Split(" ")[0]
|
||||||
|
if (-not $WslIp) { Write-Error "Could not auto-detect the WSL IP; pass -WslIp."; exit 1 }
|
||||||
|
}
|
||||||
|
Write-Host "WSL address: $WslIp"
|
||||||
|
|
||||||
|
# listenport => connectport ($RouterPort maps DOWN to the collector's 5000)
|
||||||
|
$maps = @{ $RouterPort = $CollectorPort; $KafkaPort = $KafkaPort }
|
||||||
|
foreach ($listen in $maps.Keys) {
|
||||||
|
netsh interface portproxy delete v4tov4 listenport=$listen listenaddress=0.0.0.0 2>$null | Out-Null
|
||||||
|
netsh interface portproxy add v4tov4 listenport=$listen listenaddress=0.0.0.0 `
|
||||||
|
connectport=$($maps[$listen]) connectaddress=$WslIp | Out-Null
|
||||||
|
Write-Host "portproxy 0.0.0.0:$listen -> ${WslIp}:$($maps[$listen])"
|
||||||
|
|
||||||
|
$ruleName = "OpenBMP $listen"
|
||||||
|
if (-not (Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue)) {
|
||||||
|
New-NetFirewallRule -DisplayName $ruleName -Direction Inbound `
|
||||||
|
-LocalPort $listen -Protocol TCP -Action Allow | Out-Null
|
||||||
|
Write-Host "firewall rule added: $ruleName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Active portproxy rules:"
|
||||||
|
netsh interface portproxy show v4tov4
|
||||||
|
Write-Host "Routers target the Windows LAN IP on port $RouterPort (see docs/router-bmp-config.md)."
|
||||||
Loading…
x
Reference in New Issue
Block a user