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>
This commit is contained in:
parent
2a82bd9a94
commit
2634aada24
2
.gitignore
vendored
2
.gitignore
vendored
@ -5,3 +5,5 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
|
gobgp/gobgpd.conf
|
||||||
|
gobgp-evpn/gobgpd.conf
|
||||||
|
|||||||
@ -33,14 +33,32 @@ policy from the cores. To stop the feed instantly without touching router
|
|||||||
config, `docker compose stop gobgp` -- the eBGP sessions drop and the full
|
config, `docker compose stop gobgp` -- the eBGP sessions drop and the full
|
||||||
table is withdrawn fleet-wide within seconds. See gobgp/README.md.
|
table is withdrawn fleet-wide within seconds. See gobgp/README.md.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import paramiko
|
import paramiko
|
||||||
|
|
||||||
|
|
||||||
|
def _env_default(key, default, dotenv=".env"):
|
||||||
|
"""Resolve a value from os.environ or the repo-root .env, else default."""
|
||||||
|
v = os.environ.get(key)
|
||||||
|
if v:
|
||||||
|
return v
|
||||||
|
try:
|
||||||
|
with open(dotenv) as fh:
|
||||||
|
for line in fh:
|
||||||
|
s = line.strip()
|
||||||
|
if s and not s.startswith("#") and s.startswith(f"{key}="):
|
||||||
|
return s.split("=", 1)[1].strip().strip('"').strip("'")
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
# GoBGP runs network_mode: host, so it sources BGP TCP from the host's real
|
# GoBGP runs network_mode: host, so it sources BGP TCP from the host's real
|
||||||
# interface IP (10.40.40.202) -- NOT its router-id 10.40.40.250. The cores
|
# interface IP -- NOT its router-id. The cores must peer with the host IP.
|
||||||
# must peer with the host IP.
|
# Resolved from $HOST_IP or the HOST_IP= line in repo-root .env.
|
||||||
GOBGP_IP = "10.40.40.202"
|
GOBGP_IP = _env_default("HOST_IP", "10.40.40.202")
|
||||||
GOBGP_AS = "65001"
|
GOBGP_AS = "65001"
|
||||||
|
|
||||||
# Additive config, built per core (asn = that core's local BGP AS:
|
# Additive config, built per core (asn = that core's local BGP AS:
|
||||||
|
|||||||
@ -18,12 +18,31 @@ Verify afterwards in OpenBMP:
|
|||||||
-c "SELECT name, ip_address, bgp_id, isconnected FROM routers ORDER BY name;"
|
-c "SELECT name, ip_address, bgp_id, isconnected FROM routers ORDER BY name;"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import paramiko
|
import paramiko
|
||||||
|
|
||||||
|
|
||||||
|
def _env_default(key, default, dotenv=".env"):
|
||||||
|
"""Resolve a value from os.environ or the repo-root .env, else default."""
|
||||||
|
v = os.environ.get(key)
|
||||||
|
if v:
|
||||||
|
return v
|
||||||
|
try:
|
||||||
|
with open(dotenv) as fh:
|
||||||
|
for line in fh:
|
||||||
|
s = line.strip()
|
||||||
|
if s and not s.startswith("#") and s.startswith(f"{key}="):
|
||||||
|
return s.split("=", 1)[1].strip().strip('"').strip("'")
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
# --- BMP collector ---------------------------------------------------------
|
# --- BMP collector ---------------------------------------------------------
|
||||||
COLLECTOR_HOST = "10.40.40.202"
|
# Resolved from $HOST_IP or the HOST_IP= line in repo-root .env.
|
||||||
|
COLLECTOR_HOST = _env_default("HOST_IP", "10.40.40.202")
|
||||||
COLLECTOR_PORT = "5000"
|
COLLECTOR_PORT = "5000"
|
||||||
|
|
||||||
# `bmp server 1` block — flat formal form, identical to the ESXi lab.
|
# `bmp server 1` block — flat formal form, identical to the ESXi lab.
|
||||||
|
|||||||
@ -476,12 +476,16 @@ services:
|
|||||||
# Host networking: the daemon uses the host's real IPv4 + IPv6 stack, so
|
# Host networking: the daemon uses the host's real IPv4 + IPv6 stack, so
|
||||||
# both the v4 and v6 eBGP sessions to AS57355 source from the host's
|
# both the v4 and v6 eBGP sessions to AS57355 source from the host's
|
||||||
# public addresses (no Docker IPv6/NAT plumbing). BMP still reaches the
|
# public addresses (no Docker IPv6/NAT plumbing). BMP still reaches the
|
||||||
# collector on 10.40.40.202:5000 (its published port).
|
# collector on $HOST_IP:5000 (its published port).
|
||||||
network_mode: host
|
network_mode: host
|
||||||
depends_on:
|
depends_on:
|
||||||
- collector
|
- collector
|
||||||
# gobgpd reads /config/gobgpd.conf; the same mount carries mrt-refresh.sh
|
# gobgpd.conf.tmpl (in git, with __HOST_IP__ placeholders) is rendered to
|
||||||
# and the cached MRT dumps it downloads.
|
# gobgpd.conf (gitignored) by setup.sh using $HOST_IP from .env. The
|
||||||
|
# gobgp image is distroless (no shell), so we cannot render at container
|
||||||
|
# start -- the rendered .conf must exist on the host before `docker
|
||||||
|
# compose up`. The same /config mount also carries mrt-refresh.sh and
|
||||||
|
# cached MRT dumps.
|
||||||
volumes:
|
volumes:
|
||||||
- ./gobgp:/config
|
- ./gobgp:/config
|
||||||
command: ["gobgpd", "-f", "/config/gobgpd.conf", "-t", "toml"]
|
command: ["gobgpd", "-f", "/config/gobgpd.conf", "-t", "toml"]
|
||||||
@ -498,6 +502,8 @@ services:
|
|||||||
image: jauderho/gobgp:v4.5.0
|
image: jauderho/gobgp:v4.5.0
|
||||||
depends_on:
|
depends_on:
|
||||||
- collector
|
- collector
|
||||||
|
# gobgpd.conf is rendered by setup.sh from gobgpd.conf.tmpl. See gobgp/
|
||||||
|
# service notes above.
|
||||||
volumes:
|
volumes:
|
||||||
- ./gobgp-evpn:/config
|
- ./gobgp-evpn:/config
|
||||||
command: ["gobgpd", "-f", "/config/gobgpd.conf", "-t", "toml"]
|
command: ["gobgpd", "-f", "/config/gobgpd.conf", "-t", "toml"]
|
||||||
@ -534,7 +540,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- psql
|
- psql
|
||||||
environment:
|
environment:
|
||||||
- PG_DSN=host=10.40.40.202 port=5432 dbname=openbmp user=openbmp password=${POSTGRES_PASSWORD:-openbmp}
|
- PG_DSN=host=${HOST_IP:-10.40.40.202} port=5432 dbname=openbmp user=openbmp password=${POSTGRES_PASSWORD:-openbmp}
|
||||||
- POLL_INTERVAL=900
|
- POLL_INTERVAL=900
|
||||||
- ROUTER_USER=webui
|
- ROUTER_USER=webui
|
||||||
- ROUTER_PASS=cisco
|
- ROUTER_PASS=cisco
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
# --- BMP export to the OpenBMP collector ------------------------------------
|
# --- BMP export to the OpenBMP collector ------------------------------------
|
||||||
[[bmp-servers]]
|
[[bmp-servers]]
|
||||||
[bmp-servers.config]
|
[bmp-servers.config]
|
||||||
address = "10.40.40.202"
|
address = "__HOST_IP__"
|
||||||
port = 5000
|
port = 5000
|
||||||
# local-rib: the injected EVPN routes live in the loc-rib (there are no
|
# local-rib: the injected EVPN routes live in the loc-rib (there are no
|
||||||
# BGP peers / no adj-rib-in), so export the local RIB.
|
# BGP peers / no adj-rib-in), so export the local RIB.
|
||||||
@ -77,7 +77,7 @@
|
|||||||
# --- Neighbor: CML CORE-01 (AS65020) ----------------------------------------
|
# --- Neighbor: CML CORE-01 (AS65020) ----------------------------------------
|
||||||
# GoBGP initiates outbound to the core's mgmt IP (reachable from the docker
|
# GoBGP initiates outbound to the core's mgmt IP (reachable from the docker
|
||||||
# host -- the cores already reach the host for BMP). GoBGP sources the session
|
# host -- the cores already reach the host for BMP). GoBGP sources the session
|
||||||
# from the host IP 10.40.40.202. eBGP multihop: the host is several hops from
|
# from the host IP __HOST_IP__. eBGP multihop: the host is several hops from
|
||||||
# the core. Default export policy (accept) re-advertises the full Bromirski
|
# the core. Default export policy (accept) re-advertises the full Bromirski
|
||||||
# table to the core. prefix-limit is a safety cap on what the core can send
|
# table to the core. prefix-limit is a safety cap on what the core can send
|
||||||
# back (its lab routes only -- small).
|
# back (its lab routes only -- small).
|
||||||
@ -164,7 +164,7 @@
|
|||||||
# routes, pre import-policy) -- consistent with the rest of the OpenBMP fleet.
|
# routes, pre import-policy) -- consistent with the rest of the OpenBMP fleet.
|
||||||
[[bmp-servers]]
|
[[bmp-servers]]
|
||||||
[bmp-servers.config]
|
[bmp-servers.config]
|
||||||
address = "10.40.40.202"
|
address = "__HOST_IP__"
|
||||||
port = 5000
|
port = 5000
|
||||||
route-monitoring-policy = "pre-policy"
|
route-monitoring-policy = "pre-policy"
|
||||||
statistics-timeout = 3600
|
statistics-timeout = 3600
|
||||||
@ -36,7 +36,8 @@ from ncclient import manager
|
|||||||
|
|
||||||
PG_DSN = os.environ.get(
|
PG_DSN = os.environ.get(
|
||||||
"PG_DSN",
|
"PG_DSN",
|
||||||
"host=10.40.40.202 port=5432 dbname=openbmp user=openbmp password=openbmp",
|
f"host={os.environ.get('HOST_IP', 'localhost')} port=5432 "
|
||||||
|
"dbname=openbmp user=openbmp password=openbmp",
|
||||||
)
|
)
|
||||||
POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL", "900"))
|
POLL_INTERVAL = int(os.environ.get("POLL_INTERVAL", "900"))
|
||||||
R_USER = os.environ.get("ROUTER_USER", "webui")
|
R_USER = os.environ.get("ROUTER_USER", "webui")
|
||||||
|
|||||||
10
setup.sh
10
setup.sh
@ -111,6 +111,16 @@ else
|
|||||||
echo "authelia/users_database.yml exists — left untouched"
|
echo "authelia/users_database.yml exists — left untouched"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# --- gobgpd.conf rendering (host-side -- gobgp image is distroless) ---------
|
||||||
|
# Render gobgp{,-evpn}/gobgpd.conf from gobgpd.conf.tmpl, substituting
|
||||||
|
# __HOST_IP__ with $HOST_IP. The rendered .conf is gitignored.
|
||||||
|
for d in gobgp gobgp-evpn; do
|
||||||
|
if [ -f "$d/gobgpd.conf.tmpl" ]; then
|
||||||
|
sed -e "s/__HOST_IP__/$HOST_IP/g" "$d/gobgpd.conf.tmpl" > "$d/gobgpd.conf"
|
||||||
|
echo "Rendered $d/gobgpd.conf (HOST_IP=$HOST_IP)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
# --- images -----------------------------------------------------------------
|
# --- images -----------------------------------------------------------------
|
||||||
echo "Pulling and building images ..."
|
echo "Pulling and building images ..."
|
||||||
docker compose pull --quiet
|
docker compose pull --quiet
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user