7 Commits

Author SHA1 Message Date
Sam
5b5593b6cc deploy.sh: teardown subcommand, fresh-host preflight, guided interactive flow
Implements the one-shot goal from the greenfield audit: tear down fully
and/or deploy cleanly on a pristine WSL instance with nothing but the
interactive script.

- './deploy.sh teardown': leave-it-down removal across ALL compose
  profiles (core/test/auth/evpn-test/scale-out -- the old reset missed the
  last two), networks, named volumes, rendered gobgpd.confs. Data wipe is
  opt-in (--wipe-data, guarded on native hosts) and preserves backups/
  unless --purge-backups; --purge-images reclaims images. Prints the
  Windows-side cleanup (wsl-portproxy.ps1 -Remove, new switch).
- fresh-host preflight: verifies the docker DAEMON (not just the client),
  offers to install docker-ce from the official repo (--install-prereqs
  for unattended), and handles the classic pristine-WSL trap: systemd off
  -> writes /etc/wsl.conf [boot] systemd=true and stops with exact
  restart instructions instead of dying cryptically.
- sudo authenticates ONCE up front (no more password prompt buried
  mid-provision); --reset now covers all profiles and preserves backups/
- guided flow: banner, Step N/6 headers, context lines explaining
  HOST_IP-vs-router-facing before the address prompts, router-facing IP
  validated as a dotted quad (a failed Windows-LAN autodetect can no
  longer write a <WINDOWS_LAN_IP> placeholder into .env), stage
  completions print green checkmarks, loud abort note on the plan
  confirm, post-deploy Verify + teardown hints.
- shared helpers moved to scripts/deploy-lib.sh; setup.sh sources it too
  (duplicate get_env/set_env/ask/confirm definitions deleted), keeping
  setup.sh as a standalone provisioning primitive.

Verified live on this host: teardown left zero obmp containers and
removed rendered configs; unattended redeploy brought the stack back.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 17:12:49 -07:00
Sam
b480615ca2 BMP port 1790 everywhere: parameterize the published collector port (finding 12)
Decision landed: routers target 1790 (IANA BMP port); the collector keeps
listening on 5000 in-container. One .env variable now drives every hop:

- compose publishes ${ROUTER_FACING_PORT:-1790}:5000 (was hardcoded 5000:5000)
- gobgpd.conf.tmpl (+ evpn) gains __BMP_PORT__; setup.sh renders it from
  ROUTER_FACING_PORT -- gobgp is host-networked, so it must chase the
  published port or the full-table feeder silently breaks
- deploy.sh + scripts/wsl-portproxy.ps1: the WSL side now publishes the
  router-facing port itself, so the portproxy forwards the same port
  end-to-end instead of down-mapping to 5000
- cml/proxmox_bmp_config.py reads ROUTER_FACING_IP/PORT from .env (HOST_IP
  fallback for pre-key .envs); xrd-node-definition.yaml baked port -> 1790
- .env.example documents ROUTER_FACING_IP/PORT; docs updated, finding 12
  closed on the repo side (routers still on 5000 need a config re-apply)

Verified live: collector republished 0.0.0.0:1790->5000, gobgp BMP session
Up to HOST_IP:1790 with the RIB dump flowing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 16:33:52 -07:00
Sam
83fd2be145 setup.sh: don't chmod -R 777 the postgres tree -- breaks re-deploys
Blanket 777 over an existing PGDATA makes 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 were unaffected since the
key is generated after the chmod. Skip postgres/ (the image entrypoint owns
its perms) and re-tighten the key/cert if a prior run already clobbered them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 15:46:53 -07:00
sam
36eae5df82 setup.sh: sync dashboard JSONs to the data-root, not just provisioning YAML
Fresh remote hosts came up with an empty Grafana: the provisioning YAML
pointed Grafana at /var/lib/grafana/dashboards/... (bind-mounted from
${OBMP_DATA_ROOT}/grafana/dashboards) but nothing on the host ever populated
that path. Grafana loaded the providers, found no JSON files, and showed
the built-in Home dashboard only.

setup.sh already copied obmp-grafana/provisioning/. -> the equivalent host
path; add the same copy for obmp-grafana/dashboards/. so a fresh host gets
all 42 committed dashboards on first run. Grafana's file provider re-scans
every 30s, so they appear without a container restart.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-20 15:25:23 -07:00
sam
4bcf368af0 setup.sh: add OBMP_AUTH_MODE for local vs authelia bootstrap
The bootstrap previously hard-required OBMP_DOMAIN and OBMP_COOKIE_DOMAIN
even when a user just wanted a local lab deployment with Grafana's built-in
login -- those vars only feed Authelia's session-cookie domain and the
public URL it lives behind. On a fresh host with no FQDN this made
./setup.sh impossible to pass without inventing dummy values.

New OBMP_AUTH_MODE=local|authelia in .env (default local) gates the FQDN
validation, Authelia secret generation, Authelia config rendering, and the
auth-profile image pull/build. setup.sh also writes GF_SERVER_ROOT_URL into
.env -- http://HOST_IP:3000/grafana/ for local, https://OBMP_DOMAIN/grafana/
for authelia -- and docker-compose.yml now reads ${GF_SERVER_ROOT_URL}
instead of hardcoding the apodacalab.com fallback.

Back-compat: an existing .env with no OBMP_AUTH_MODE but a real OBMP_DOMAIN
or an existing AUTHELIA_SESSION_SECRET is inferred as 'authelia' and the
mode is persisted -- a re-run on a live Authelia host won't silently flip
it to local and break the next docker compose up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-24 13:35:22 -07:00
sam
2634aada24 Parameterize HOST_IP everywhere -- portable to another lab host
Removes hardcoded 10.40.40.202 references so a fresh clone + .env-only
edit can stand the stack up on a new compute node.

  * docker-compose.yml: rib-poller PG_DSN now uses ${HOST_IP:-...}.
  * obmp-rib-poller/poller.py: default PG_DSN host falls back to
    ${HOST_IP} env (compose passes it; manual runs honour $HOST_IP too).
  * cml/gobgp_peering_config.py: GOBGP_IP read from $HOST_IP or the
    HOST_IP= line in repo-root .env, with a small _env_default helper.
  * cml/proxmox_bmp_config.py: COLLECTOR_HOST resolved the same way.

For gobgp/gobgpd.conf and gobgp-evpn/gobgpd.conf -- jauderho/gobgp is
distroless (no shell), so we can't sed-substitute at container start.
Pattern instead:

  * gobgpd.conf is now gobgpd.conf.tmpl with __HOST_IP__ placeholders
    (committed). The rendered gobgpd.conf is gitignored.
  * setup.sh renders the .tmpl(s) to .conf using $HOST_IP from .env.
  * compose `command` stays the simple `gobgpd -f /config/gobgpd.conf`.

After cloning on a new host:  cp .env.example .env  -> edit HOST_IP ->
./setup.sh -> docker compose up -d. Verified locally by force-recreating
gobgp; all 6 sessions (4 cores + 2 Bromirski) re-established in <60s.

Known portability gaps still to address (separate work):
  * Hardcoded lab-router inventories in cml/*.py and
    obmp-rib-poller/poller.py.
  * The /etc/cron.d/openbmp */5 -> */15 edit inside obmp-psql-app is
    not persistent (regenerated by config_cron on every container start).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-19 18:34:51 -07:00
sam
cf4e5b07c6 Add Compose profiles, setup.sh bootstrap, and config templates for portable deployment
Pins the Compose project name and splits services into core / test / auth
profiles so the BMP collector core can deploy standalone. Adds setup.sh
(idempotent bootstrap), .env.example, and repo-resident Authelia config
templates so a fresh host deploys without manual steps. Parameterizes
hardcoded host IP and domain; points the Grafana InfluxDB datasource at the
container name.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 19:21:04 -07:00