Compare commits
3 Commits
b480615ca2
...
5b5593b6cc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b5593b6cc | ||
|
|
da975d7375 | ||
|
|
65fd88a0a2 |
14
README.md
14
README.md
@ -1,6 +1,14 @@
|
|||||||
# OpenBMP docker files
|
# OpenBMP docker files
|
||||||
Docker files for OpenBMP.
|
Docker files for OpenBMP.
|
||||||
|
|
||||||
|
> **Start here:**
|
||||||
|
> - **Deploying the stack?** Jump to [Greenfield deploy](#greenfield-deploy-recommended-deploysh)
|
||||||
|
> below, then see the [docs index](docs/README.md) for deployment types,
|
||||||
|
> portability findings, sizing, and backup.
|
||||||
|
> - **Configuring routers to feed it?** Go straight to
|
||||||
|
> [docs/router-integration.md](docs/router-integration.md) and the
|
||||||
|
> copy-paste fragments in [router-blueprints/](router-blueprints/).
|
||||||
|
|
||||||
## (Prerequisite) Platform Docker Install
|
## (Prerequisite) Platform Docker Install
|
||||||
|
|
||||||
> Ignore this step if you already have a current docker install
|
> Ignore this step if you already have a current docker install
|
||||||
@ -104,8 +112,10 @@ sudo chmod -R 7777 $OBMP_DATA_ROOT
|
|||||||
> refuse to start. Skip `postgres/` (setup.sh does this for you — see
|
> refuse to start. Skip `postgres/` (setup.sh does this for you — see
|
||||||
> [docs/PORTABILITY-FINDINGS.md](docs/PORTABILITY-FINDINGS.md) finding 10).
|
> [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
|
> DB tables are created **automatically** by psql-app on its first run (it
|
||||||
> only be done once or whenever you want to completely wipe out the DB and start over.
|
> drops a `config/do_not_init_db` marker afterward so restarts skip the
|
||||||
|
> migration). No `init_db` trigger file is needed — that was upstream
|
||||||
|
> behavior this repo's psql-app replaces.
|
||||||
|
|
||||||
Change ```OBMP_DATA_ROOT=<path>``` to where you created the directories above. The default is ```/var/openbmp```
|
Change ```OBMP_DATA_ROOT=<path>``` to where you created the directories above. The default is ```/var/openbmp```
|
||||||
|
|
||||||
|
|||||||
@ -130,12 +130,11 @@ configuration:
|
|||||||
!
|
!
|
||||||
bmp server 1
|
bmp server 1
|
||||||
host 10.40.40.202 port 1790
|
host 10.40.40.202 port 1790
|
||||||
description OpenBMP
|
description OpenBMP-Collector
|
||||||
update-source Gi0/0/0/0
|
update-source Gi0/0/0/0
|
||||||
flapping-delay 60
|
initial-delay 60
|
||||||
initial-delay 5
|
|
||||||
stats-reporting-period 300
|
stats-reporting-period 300
|
||||||
initial-refresh delay 30 spread 2
|
initial-refresh delay 60 spread 30
|
||||||
!
|
!
|
||||||
ssh server v2
|
ssh server v2
|
||||||
end
|
end
|
||||||
|
|||||||
270
deploy.sh
270
deploy.sh
@ -17,14 +17,22 @@
|
|||||||
# ./deploy.sh # interactive; auto-detect + prompts
|
# ./deploy.sh # interactive; auto-detect + prompts
|
||||||
# ./deploy.sh --wsl | --prod # force host type (still prompts rest)
|
# ./deploy.sh --wsl | --prod # force host type (still prompts rest)
|
||||||
# ./deploy.sh --host-ip 10.0.0.50 # preset HOST_IP (internal stack IP)
|
# ./deploy.sh --host-ip 10.0.0.50 # preset HOST_IP (internal stack IP)
|
||||||
# ./deploy.sh --router-ip 10.0.0.9 --router-port 5000 # BMP-facing target
|
# ./deploy.sh --router-ip 10.0.0.9 --router-port 1790 # BMP-facing target
|
||||||
# ./deploy.sh --auth local|authelia
|
# ./deploy.sh --auth local|authelia
|
||||||
# ./deploy.sh --scope core|feeders|full|standalone
|
# ./deploy.sh --scope full-stack|remote|central-store
|
||||||
# ./deploy.sh --central-kafka 10.0.0.50:9092 # for --scope standalone
|
# ./deploy.sh --central-kafka 10.0.0.50:9092 # for --scope remote
|
||||||
# ./deploy.sh --reset # wipe data tree first (guarded on prod)
|
# ./deploy.sh --reset # wipe data tree first (guarded on prod)
|
||||||
|
# ./deploy.sh --install-prereqs # allow installing docker-ce unattended
|
||||||
# ./deploy.sh --prod --yes # unattended (needs enough presets)
|
# ./deploy.sh --prod --yes # unattended (needs enough presets)
|
||||||
# REPO_DIR=/opt/obmp-docker ./deploy.sh
|
# REPO_DIR=/opt/obmp-docker ./deploy.sh
|
||||||
#
|
#
|
||||||
|
# ./deploy.sh teardown # stop + remove EVERYTHING, leave it down
|
||||||
|
# --wipe-data # also wipe $OBMP_DATA_ROOT (guarded;
|
||||||
|
# # backups/ preserved by default)
|
||||||
|
# --purge-images # also remove stack images
|
||||||
|
# --purge-backups # do not preserve backups/ on wipe
|
||||||
|
# --yes # unattended (requires explicit flags)
|
||||||
|
#
|
||||||
# SCOPE / DEPLOYMENT LEVELS (what each includes, excludes, ballpark sysreqs):
|
# SCOPE / DEPLOYMENT LEVELS (what each includes, excludes, ballpark sysreqs):
|
||||||
#
|
#
|
||||||
# core Collector core: zookeeper, kafka, psql, collector, psql-app,
|
# core Collector core: zookeeper, kafka, psql, collector, psql-app,
|
||||||
@ -103,7 +111,7 @@
|
|||||||
# Mitigations: NVMe with real IOPS; BMP-monitor pre-policy on the RRs only
|
# Mitigations: NVMe with real IOPS; BMP-monitor pre-policy on the RRs only
|
||||||
# (not every client session) to avoid ingesting N reflected copies of the
|
# (not every client session) to avoid ingesting N reflected copies of the
|
||||||
# same table; stagger connects (initial-refresh delay/spread); raise
|
# same table; stagger connects (initial-refresh delay/spread); raise
|
||||||
# KAFKA/PSQL_APP mem; or split ingest with --scope standalone collectors.
|
# KAFKA/PSQL_APP mem; or split ingest with --scope remote collectors.
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# HOST TYPE affects default HOST_IP source, default auth/scope, reset guarding,
|
# HOST TYPE affects default HOST_IP source, default auth/scope, reset guarding,
|
||||||
@ -119,9 +127,19 @@ ARG_ROUTER_IP=""
|
|||||||
ARG_ROUTER_PORT=""
|
ARG_ROUTER_PORT=""
|
||||||
ARG_AUTH="" # local | authelia
|
ARG_AUTH="" # local | authelia
|
||||||
ARG_SCOPE="" # full-stack | remote | central-store (aliases accepted)
|
ARG_SCOPE="" # full-stack | remote | central-store (aliases accepted)
|
||||||
ARG_CENTRAL_KAFKA="" # host:port for --scope standalone
|
ARG_CENTRAL_KAFKA="" # host:port for --scope remote
|
||||||
ARG_RESET=0
|
ARG_RESET=0
|
||||||
ARG_YES=0
|
ARG_YES=0
|
||||||
|
ARG_INSTALL_PREREQS=0
|
||||||
|
COMMAND="deploy" # deploy (default) | teardown
|
||||||
|
ARG_WIPE_DATA=0 # teardown: also wipe $OBMP_DATA_ROOT
|
||||||
|
ARG_PURGE_IMAGES=0 # teardown: also remove stack images
|
||||||
|
ARG_PURGE_BACKUPS=0 # teardown: do NOT preserve $OBMP_DATA_ROOT/backups
|
||||||
|
|
||||||
|
# First positional arg may be a subcommand.
|
||||||
|
case "${1:-}" in
|
||||||
|
teardown) COMMAND="teardown"; shift ;;
|
||||||
|
esac
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@ -140,6 +158,10 @@ while [ $# -gt 0 ]; do
|
|||||||
--central-kafka) ARG_CENTRAL_KAFKA="${2:?--central-kafka needs host:port}"; shift ;;
|
--central-kafka) ARG_CENTRAL_KAFKA="${2:?--central-kafka needs host:port}"; shift ;;
|
||||||
--central-kafka=*) ARG_CENTRAL_KAFKA="${1#*=}" ;;
|
--central-kafka=*) ARG_CENTRAL_KAFKA="${1#*=}" ;;
|
||||||
--reset) ARG_RESET=1 ;;
|
--reset) ARG_RESET=1 ;;
|
||||||
|
--install-prereqs) ARG_INSTALL_PREREQS=1 ;;
|
||||||
|
--wipe-data) ARG_WIPE_DATA=1 ;;
|
||||||
|
--purge-images) ARG_PURGE_IMAGES=1 ;;
|
||||||
|
--purge-backups) ARG_PURGE_BACKUPS=1 ;;
|
||||||
--yes|-y) ARG_YES=1 ;;
|
--yes|-y) ARG_YES=1 ;;
|
||||||
-h|--help) grep '^#' "$0" | sed 's/^#//'; exit 0 ;;
|
-h|--help) grep '^#' "$0" | sed 's/^#//'; exit 0 ;;
|
||||||
*) echo "Unknown arg: $1" >&2; exit 1 ;;
|
*) echo "Unknown arg: $1" >&2; exit 1 ;;
|
||||||
@ -147,10 +169,9 @@ while [ $# -gt 0 ]; do
|
|||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
log() { printf '\033[1;36m[deploy]\033[0m %s\n' "$*"; }
|
# Shared output/prompt/.env helpers (log ok warn die hr banner step ask
|
||||||
warn() { printf '\033[1;33m[deploy][warn]\033[0m %s\n' "$*" >&2; }
|
# confirm get_env set_env is_ipv4) -- single definition for all deploy tooling.
|
||||||
die() { printf '\033[1;31m[deploy][err]\033[0m %s\n' "$*" >&2; exit 1; }
|
. "$REPO_DIR/scripts/deploy-lib.sh"
|
||||||
hr() { printf '\033[2m%s\033[0m\n' "----------------------------------------------------------------"; }
|
|
||||||
|
|
||||||
# Return the host-published TCP ports this deployment type will bind, so we can
|
# Return the host-published TCP ports this deployment type will bind, so we can
|
||||||
# check them for collisions BEFORE bring-up. Ports come from docker-compose.yml
|
# check them for collisions BEFORE bring-up. Ports come from docker-compose.yml
|
||||||
@ -225,32 +246,71 @@ preflight_ports() {
|
|||||||
return $conflict
|
return $conflict
|
||||||
}
|
}
|
||||||
|
|
||||||
# Prompt with an editable default. Honors --yes (accepts default silently).
|
|
||||||
ask() {
|
|
||||||
local __var="$1" __prompt="$2" __default="${3:-}" __reply=""
|
|
||||||
if [ "$ARG_YES" -eq 1 ]; then
|
|
||||||
printf -v "$__var" '%s' "$__default"; return
|
|
||||||
fi
|
|
||||||
if [ -n "$__default" ]; then
|
|
||||||
read -r -p "$__prompt [$__default]: " __reply
|
|
||||||
__reply="${__reply:-$__default}"
|
|
||||||
else
|
|
||||||
read -r -p "$__prompt: " __reply
|
|
||||||
fi
|
|
||||||
printf -v "$__var" '%s' "$__reply"
|
|
||||||
}
|
|
||||||
|
|
||||||
confirm() {
|
|
||||||
local __prompt="$1" __reply=""
|
|
||||||
[ "$ARG_YES" -eq 1 ] && return 0
|
|
||||||
read -r -p "$__prompt [y/N]: " __reply
|
|
||||||
[ "$__reply" = "y" ] || [ "$__reply" = "Y" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
cd "$REPO_DIR" || die "REPO_DIR '$REPO_DIR' not found"
|
cd "$REPO_DIR" || die "REPO_DIR '$REPO_DIR' not found"
|
||||||
[ -f docker-compose.yml ] || die "no docker-compose.yml in $REPO_DIR - set REPO_DIR"
|
[ -f docker-compose.yml ] || die "no docker-compose.yml in $REPO_DIR - set REPO_DIR"
|
||||||
command -v docker >/dev/null || die "docker not found"
|
|
||||||
docker compose version >/dev/null 2>&1 || die "docker compose v2 not available"
|
# --- host prerequisites (fresh-WSL friendly) --------------------------------
|
||||||
|
# A pristine WSL2 Ubuntu has neither docker-ce nor systemd enabled; check the
|
||||||
|
# whole chain and either remediate (--install-prereqs / interactive offer) or
|
||||||
|
# print the exact fix instead of dying with a bare message mid-run.
|
||||||
|
install_docker_ce() {
|
||||||
|
log "Installing docker-ce from the official Docker apt repo ..."
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq ca-certificates curl
|
||||||
|
sudo install -m 0755 -d /etc/apt/keyrings
|
||||||
|
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
|
||||||
|
sudo chmod a+r /etc/apt/keyrings/docker.asc
|
||||||
|
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
|
||||||
|
| sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
ok "docker-ce installed"
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_systemd_wsl() {
|
||||||
|
# Native docker in WSL2 needs systemd (the docker service). Pristine
|
||||||
|
# distros ship with it off; flipping it requires a WSL restart, which we
|
||||||
|
# cannot do from inside -- set it up and stop with clear instructions.
|
||||||
|
[ -d /run/systemd/system ] && return 0
|
||||||
|
grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null || return 0
|
||||||
|
warn "systemd is not running -- native docker cannot start without it."
|
||||||
|
if confirm "Enable systemd in /etc/wsl.conf now (needs a WSL restart afterwards)?"; then
|
||||||
|
if ! grep -q '^\[boot\]' /etc/wsl.conf 2>/dev/null; then
|
||||||
|
printf '\n[boot]\nsystemd=true\n' | sudo tee -a /etc/wsl.conf > /dev/null
|
||||||
|
elif ! grep -q '^systemd=true' /etc/wsl.conf; then
|
||||||
|
sudo sed -i '/^\[boot\]/a systemd=true' /etc/wsl.conf
|
||||||
|
fi
|
||||||
|
ok "systemd enabled in /etc/wsl.conf"
|
||||||
|
die "now run 'wsl --shutdown' from Windows, reopen this distro, and re-run ./deploy.sh"
|
||||||
|
fi
|
||||||
|
die "cannot continue without systemd on WSL (docker service will not start)"
|
||||||
|
}
|
||||||
|
|
||||||
|
preflight_host() {
|
||||||
|
if ! command -v docker >/dev/null; then
|
||||||
|
warn "docker is not installed on this host."
|
||||||
|
if [ "$ARG_INSTALL_PREREQS" -eq 1 ] || confirm "Install docker-ce now (official Docker apt repo)?"; then
|
||||||
|
ensure_systemd_wsl
|
||||||
|
install_docker_ce
|
||||||
|
sudo systemctl enable --now docker 2>/dev/null || true
|
||||||
|
else
|
||||||
|
die "install docker-ce first (https://docs.docker.com/engine/install/ubuntu/) or re-run with --install-prereqs"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
docker compose version >/dev/null 2>&1 \
|
||||||
|
|| die "docker compose v2 missing - install the docker-compose-plugin package"
|
||||||
|
if ! docker info >/dev/null 2>&1; then
|
||||||
|
# client exists but daemon unreachable: systemd off (WSL) or service down
|
||||||
|
ensure_systemd_wsl
|
||||||
|
warn "docker daemon is not running - trying to start it ..."
|
||||||
|
sudo systemctl enable --now docker 2>/dev/null || true
|
||||||
|
sleep 2
|
||||||
|
docker info >/dev/null 2>&1 \
|
||||||
|
|| die "docker daemon still unreachable - check: systemctl status docker"
|
||||||
|
ok "docker daemon started"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
preflight_host
|
||||||
|
|
||||||
# --- Docker Desktop vs native-daemon guard ----------------------------------
|
# --- Docker Desktop vs native-daemon guard ----------------------------------
|
||||||
# On WSL, Docker Desktop runs the daemon in its OWN separate distro
|
# On WSL, Docker Desktop runs the daemon in its OWN separate distro
|
||||||
@ -285,23 +345,97 @@ if detect_docker_desktop; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- .env helpers -----------------------------------------------------------
|
# --- teardown subcommand -----------------------------------------------------
|
||||||
|
# Leave-it-down teardown: removes every container across ALL compose profiles,
|
||||||
|
# the named volumes, and the rendered gitignored configs. Data-tree wipe is
|
||||||
|
# opt-in (--wipe-data or interactive), preserving backups/ unless
|
||||||
|
# --purge-backups. Images are kept unless --purge-images. Prints the
|
||||||
|
# Windows-side cleanup it cannot reach from inside WSL.
|
||||||
|
ALL_PROFILES=(--profile test --profile auth --profile evpn-test --profile scale-out)
|
||||||
|
cmd_teardown() {
|
||||||
|
banner "OpenBMP stack teardown"
|
||||||
|
local dr; dr="$( [ -f .env ] && get_env OBMP_DATA_ROOT || true )"; dr="${dr:-/var/openbmp}"
|
||||||
|
echo
|
||||||
|
log "This will remove:"
|
||||||
|
echo " - all obmp containers (every profile: core, test, auth, evpn-test, scale-out)"
|
||||||
|
echo " - docker networks + named volumes (obmp_data-volume, obmp_ts-volume)"
|
||||||
|
echo " - rendered configs (gobgp/gobgpd.conf, gobgp-evpn/gobgpd.conf)"
|
||||||
|
[ "$ARG_PURGE_IMAGES" -eq 1 ] && echo " - stack images (--purge-images)"
|
||||||
|
if [ "$ARG_WIPE_DATA" -eq 1 ]; then
|
||||||
|
echo " - DATA TREE $dr (--wipe-data)$( [ "$ARG_PURGE_BACKUPS" -eq 1 ] && echo ' INCLUDING backups/' || echo ', preserving backups/')"
|
||||||
|
else
|
||||||
|
echo " - (data tree $dr is KEPT; add --wipe-data for a full wipe)"
|
||||||
|
fi
|
||||||
|
echo " - .env is always kept"
|
||||||
|
echo
|
||||||
|
confirm "Tear the stack down?" || die "aborted - nothing was removed"
|
||||||
|
|
||||||
|
log "Stopping and removing containers (all profiles) ..."
|
||||||
|
if [ "$ARG_PURGE_IMAGES" -eq 1 ]; then
|
||||||
|
docker compose "${ALL_PROFILES[@]}" down -v --remove-orphans --rmi all 2>&1 | tail -3 || true
|
||||||
|
else
|
||||||
|
docker compose "${ALL_PROFILES[@]}" down -v --remove-orphans 2>&1 | tail -3 || true
|
||||||
|
fi
|
||||||
|
docker volume rm obmp_data-volume obmp_ts-volume 2>/dev/null || true
|
||||||
|
ok "containers, networks, volumes removed"
|
||||||
|
|
||||||
|
rm -f gobgp/gobgpd.conf gobgp-evpn/gobgpd.conf 2>/dev/null || true
|
||||||
|
ok "rendered configs removed"
|
||||||
|
|
||||||
|
if [ "$ARG_WIPE_DATA" -eq 1 ] || { [ "$ARG_YES" -eq 0 ] && confirm "ALSO wipe the data tree $dr (Postgres history, Kafka spool)?"; }; then
|
||||||
|
if [ -d "$dr" ]; then
|
||||||
|
if grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then :; else
|
||||||
|
warn "Native host data wipe - this destroys all BMP history under $dr."
|
||||||
|
[ "$ARG_YES" -eq 1 ] && [ "$ARG_WIPE_DATA" -eq 0 ] && die "refusing implicit data wipe under --yes; pass --wipe-data explicitly"
|
||||||
|
if [ "$ARG_YES" -eq 0 ]; then
|
||||||
|
read -r -p "Type the data root path to confirm deletion: " __c
|
||||||
|
[ "$__c" = "$dr" ] || die "confirmation did not match - data tree untouched"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$ARG_PURGE_BACKUPS" -eq 1 ]; then
|
||||||
|
sudo rm -rf "${dr:?}"/* 2>/dev/null || true
|
||||||
|
ok "data tree wiped (including backups)"
|
||||||
|
else
|
||||||
|
sudo find "$dr" -mindepth 1 -maxdepth 1 ! -name backups -exec rm -rf {} + 2>/dev/null || true
|
||||||
|
ok "data tree wiped (backups/ preserved)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
hr
|
||||||
|
log "Linux-side teardown complete. Windows-side leftovers (if this was a"
|
||||||
|
log "WSL deployment) need an ELEVATED PowerShell:"
|
||||||
|
echo
|
||||||
|
echo " powershell -ExecutionPolicy Bypass -File scripts\\wsl-portproxy.ps1 -Remove"
|
||||||
|
echo
|
||||||
|
log "Verify clean: docker ps -a | grep obmp (expect nothing)"
|
||||||
|
hr
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
[ "$COMMAND" = "teardown" ] && cmd_teardown
|
||||||
|
|
||||||
|
banner "OpenBMP stack deploy"
|
||||||
|
|
||||||
|
# --- .env bootstrap ----------------------------------------------------------
|
||||||
if [ ! -f .env ]; then
|
if [ ! -f .env ]; then
|
||||||
[ -f .env.example ] || die "no .env or .env.example present"
|
[ -f .env.example ] || die "no .env or .env.example present"
|
||||||
cp .env.example .env
|
cp .env.example .env
|
||||||
log "Created .env from .env.example"
|
log "Created .env from .env.example"
|
||||||
fi
|
fi
|
||||||
get_env() { grep -E "^$1=" .env | head -1 | cut -d= -f2- || true; }
|
|
||||||
set_env() {
|
# Prime sudo early if filesystem setup will need it, so the password prompt
|
||||||
local key="$1" val="$2"
|
# happens HERE (visible, at the start) instead of blocking mid-provision.
|
||||||
if grep -qE "^${key}=" .env; then
|
_dr_probe="$(get_env OBMP_DATA_ROOT)"; _dr_probe="${_dr_probe:-/var/openbmp}"
|
||||||
sed -i "s|^${key}=.*|${key}=${val}|" .env
|
if [ ! -w "$(dirname "$_dr_probe")" ] || { [ -d "$_dr_probe" ] && [ ! -w "$_dr_probe" ]; }; then
|
||||||
else
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
printf '%s=%s\n' "$key" "$val" >> .env
|
log "Filesystem setup under $_dr_probe needs sudo - authenticating now."
|
||||||
|
sudo -v || die "sudo authentication failed"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
|
||||||
|
|
||||||
# --- 1. host type: auto-detect, then override -------------------------------
|
# --- 1. host type: auto-detect, then override -------------------------------
|
||||||
|
step 1 6 "Host type"
|
||||||
detected="prod"
|
detected="prod"
|
||||||
if grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then detected="wsl"; fi
|
if grep -qiE 'microsoft|wsl' /proc/version 2>/dev/null; then detected="wsl"; fi
|
||||||
if [ -n "$ARG_HOSTTYPE" ]; then
|
if [ -n "$ARG_HOSTTYPE" ]; then
|
||||||
@ -309,6 +443,8 @@ if [ -n "$ARG_HOSTTYPE" ]; then
|
|||||||
log "Host type: $HOSTTYPE (from flag; auto-detect said '$detected')"
|
log "Host type: $HOSTTYPE (from flag; auto-detect said '$detected')"
|
||||||
else
|
else
|
||||||
log "Auto-detected host type: $detected"
|
log "Auto-detected host type: $detected"
|
||||||
|
echo " wsl = this distro runs under WSL2 (routers reach the stack via the Windows portproxy)"
|
||||||
|
echo " prod = native Linux host (routers reach the stack directly)"
|
||||||
ask HOSTTYPE "Host type (wsl/prod)" "$detected"
|
ask HOSTTYPE "Host type (wsl/prod)" "$detected"
|
||||||
fi
|
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
|
||||||
@ -355,6 +491,7 @@ normalize_type() {
|
|||||||
*) echo "" ;;
|
*) echo "" ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
step 2 6 "Deployment type"
|
||||||
if [ -n "$ARG_SCOPE" ]; then
|
if [ -n "$ARG_SCOPE" ]; then
|
||||||
DTYPE="$(normalize_type "$ARG_SCOPE")"
|
DTYPE="$(normalize_type "$ARG_SCOPE")"
|
||||||
[ -n "$DTYPE" ] || die "unknown --scope '$ARG_SCOPE' (use full-stack|remote|central-store)"
|
[ -n "$DTYPE" ] || die "unknown --scope '$ARG_SCOPE' (use full-stack|remote|central-store)"
|
||||||
@ -371,6 +508,12 @@ log "Deployment type: $DTYPE"
|
|||||||
# --- 3. HOST_IP: smart default, editable, validated -------------------------
|
# --- 3. HOST_IP: smart default, editable, validated -------------------------
|
||||||
# HOST_IP is the address the INTERNAL stack advertises/binds (Kafka listener,
|
# HOST_IP is the address the INTERNAL stack advertises/binds (Kafka listener,
|
||||||
# gobgp->collector). On a remote collector it is also this node's own address.
|
# gobgp->collector). On a remote collector it is also this node's own address.
|
||||||
|
step 3 6 "Addresses (internal bind + what routers target)"
|
||||||
|
echo " Two different addresses are about to be asked for:"
|
||||||
|
echo " HOST_IP = where the stack binds INTERNALLY (Kafka, gobgp). On WSL"
|
||||||
|
echo " this is the WSL VM address."
|
||||||
|
echo " Router-facing = what routers put in 'bmp server host ...'. On WSL this"
|
||||||
|
echo " is the WINDOWS LAN IP (portproxy forwards it inward)."
|
||||||
primary_ip="$(ip -o -4 addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1 || true)"
|
primary_ip="$(ip -o -4 addr show scope global 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | head -1 || true)"
|
||||||
wsl_ip="$(hostname -I 2>/dev/null | awk '{print $1}' || true)"
|
wsl_ip="$(hostname -I 2>/dev/null | awk '{print $1}' || true)"
|
||||||
current_ip="$(get_env HOST_IP)"
|
current_ip="$(get_env HOST_IP)"
|
||||||
@ -461,6 +604,13 @@ BMP_STD_PORT=1790
|
|||||||
default_router_port="${ARG_ROUTER_PORT:-$BMP_STD_PORT}"
|
default_router_port="${ARG_ROUTER_PORT:-$BMP_STD_PORT}"
|
||||||
if [ -n "$ARG_ROUTER_IP" ]; then ROUTER_IP="$ARG_ROUTER_IP"; else
|
if [ -n "$ARG_ROUTER_IP" ]; then ROUTER_IP="$ARG_ROUTER_IP"; else
|
||||||
ask ROUTER_IP "Router-facing IP (what 'bmp server' on the routers targets)" "$default_router_ip"
|
ask ROUTER_IP "Router-facing IP (what 'bmp server' on the routers targets)" "$default_router_ip"
|
||||||
|
# A failed Windows-LAN-IP autodetect leaves a <WINDOWS_LAN_IP> placeholder
|
||||||
|
# as the default; don't let a non-address sail into .env silently.
|
||||||
|
while ! is_ipv4 "$ROUTER_IP"; do
|
||||||
|
[ "$ARG_YES" -eq 1 ] && die "router-facing IP '$ROUTER_IP' is not an IPv4 address (autodetect failed?) - pass --router-ip"
|
||||||
|
warn "'$ROUTER_IP' is not an IPv4 address."
|
||||||
|
ask ROUTER_IP "Router-facing IP (dotted quad)" ""
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
if [ -n "$ARG_ROUTER_PORT" ]; then ROUTER_PORT="$ARG_ROUTER_PORT"; else
|
if [ -n "$ARG_ROUTER_PORT" ]; then ROUTER_PORT="$ARG_ROUTER_PORT"; else
|
||||||
ask ROUTER_PORT "Router-facing BMP port" "$default_router_port"
|
ask ROUTER_PORT "Router-facing BMP port" "$default_router_port"
|
||||||
@ -480,6 +630,7 @@ if [ "$DTYPE" = "remote" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 6. Authelia toggle (independent axis; N/A for remote - no local UI) -----
|
# --- 6. Authelia toggle (independent axis; N/A for remote - no local UI) -----
|
||||||
|
step 4 6 "Grafana authentication"
|
||||||
AUTH_MODE="local"
|
AUTH_MODE="local"
|
||||||
OBMP_DOMAIN="$(get_env OBMP_DOMAIN)"
|
OBMP_DOMAIN="$(get_env OBMP_DOMAIN)"
|
||||||
OBMP_COOKIE_DOMAIN="$(get_env OBMP_COOKIE_DOMAIN)"
|
OBMP_COOKIE_DOMAIN="$(get_env OBMP_COOKIE_DOMAIN)"
|
||||||
@ -526,6 +677,7 @@ type_resources() {
|
|||||||
central-store) echo "~16 vCPU / 48-64 GB / NVMe >=250 GB (carries all remotes' data)" ;;
|
central-store) echo "~16 vCPU / 48-64 GB / NVMe >=250 GB (carries all remotes' data)" ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
|
step 5 6 "Plan review"
|
||||||
echo; hr; log "Deployment plan"; hr
|
echo; hr; log "Deployment plan"; hr
|
||||||
# Record what is being deployed - a greenfield deploy is only reproducible if
|
# 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).
|
# the checkout is pinned (branch + commit go in the plan and the terminal log).
|
||||||
@ -568,9 +720,11 @@ else
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! confirm "Proceed with this plan?"; then
|
# NOTE: default answer is No -- hitting Enter here ABORTS (safe default).
|
||||||
die "aborted before making changes - nothing was modified"
|
if ! confirm "Proceed with this plan? (y = deploy, Enter/n = abort)"; then
|
||||||
|
die "aborted before making changes - nothing was modified (answer 'y' to deploy)"
|
||||||
fi
|
fi
|
||||||
|
step 6 6 "Provision + staged bring-up"
|
||||||
|
|
||||||
# --- 8. write .env ----------------------------------------------------------
|
# --- 8. write .env ----------------------------------------------------------
|
||||||
set_env HOST_IP "$HOST_IP"
|
set_env HOST_IP "$HOST_IP"
|
||||||
@ -598,10 +752,13 @@ if [ "$ARG_RESET" -eq 1 ]; then
|
|||||||
else
|
else
|
||||||
warn "Resetting data tree under $OBMP_DATA_ROOT (WSL test host)"
|
warn "Resetting data tree under $OBMP_DATA_ROOT (WSL test host)"
|
||||||
fi
|
fi
|
||||||
docker compose --profile test --profile auth down -v 2>/dev/null || true
|
docker compose "${ALL_PROFILES[@]}" down -v --remove-orphans 2>/dev/null || true
|
||||||
docker volume rm obmp_data-volume obmp_ts-volume 2>/dev/null || true
|
docker volume rm obmp_data-volume obmp_ts-volume 2>/dev/null || true
|
||||||
sudo rm -rf "${OBMP_DATA_ROOT:?}/"* 2>/dev/null || true
|
# Wipe the tree but keep backups/ -- pg-backup.sh dumps live there and a
|
||||||
log "Reset complete"
|
# reset should not silently destroy them (use 'teardown --purge-backups'
|
||||||
|
# for that).
|
||||||
|
sudo find "${OBMP_DATA_ROOT:?}" -mindepth 1 -maxdepth 1 ! -name backups -exec rm -rf {} + 2>/dev/null || true
|
||||||
|
log "Reset complete (backups/ preserved if present)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 10. run setup.sh -------------------------------------------------------
|
# --- 10. run setup.sh -------------------------------------------------------
|
||||||
@ -642,7 +799,7 @@ wait_kafka() {
|
|||||||
done
|
done
|
||||||
[ "$(docker inspect obmp-kafka --format '{{.State.Status}}' 2>/dev/null)" = running ] \
|
[ "$(docker inspect obmp-kafka --format '{{.State.Status}}' 2>/dev/null)" = running ] \
|
||||||
|| die "kafka did not stabilise - check: docker logs obmp-kafka"
|
|| die "kafka did not stabilise - check: docker logs obmp-kafka"
|
||||||
log " kafka up"
|
ok "kafka up"
|
||||||
}
|
}
|
||||||
wait_postgres() {
|
wait_postgres() {
|
||||||
log " waiting for postgres ..."
|
log " waiting for postgres ..."
|
||||||
@ -658,7 +815,7 @@ if [ "$DTYPE" = "remote" ]; then
|
|||||||
wait_kafka
|
wait_kafka
|
||||||
log "Stage 2/2 - collector (forwarding to $CENTRAL_KAFKA) ..."
|
log "Stage 2/2 - collector (forwarding to $CENTRAL_KAFKA) ..."
|
||||||
docker compose up -d collector
|
docker compose up -d collector
|
||||||
log " collector up"
|
ok "collector up"
|
||||||
|
|
||||||
elif [ "$DTYPE" = "central-store" ]; then
|
elif [ "$DTYPE" = "central-store" ]; then
|
||||||
# Store only: Postgres/psql-app/Grafana/feeders, NO local collector.
|
# Store only: Postgres/psql-app/Grafana/feeders, NO local collector.
|
||||||
@ -668,12 +825,12 @@ elif [ "$DTYPE" = "central-store" ]; then
|
|||||||
docker compose up -d psql
|
docker compose up -d psql
|
||||||
wait_postgres
|
wait_postgres
|
||||||
docker compose up -d psql-app grafana whois
|
docker compose up -d psql-app grafana whois
|
||||||
log " store up"
|
ok "store up"
|
||||||
log "Stage 3/3 - feeders/consumers (--profile test) ..."
|
log "Stage 3/3 - feeders/consumers (--profile test) ..."
|
||||||
docker compose --profile test up -d
|
docker compose --profile test up -d
|
||||||
# ensure no local collector is running on a central-store node
|
# ensure no local collector is running on a central-store node
|
||||||
docker compose stop collector 2>/dev/null || true
|
docker compose stop collector 2>/dev/null || true
|
||||||
log " feeders up (no local collector)"
|
ok "feeders up (no local collector)"
|
||||||
|
|
||||||
else
|
else
|
||||||
# full-stack: collector + store + feeders on one host.
|
# full-stack: collector + store + feeders on one host.
|
||||||
@ -686,10 +843,10 @@ else
|
|||||||
docker compose up -d psql
|
docker compose up -d psql
|
||||||
wait_postgres
|
wait_postgres
|
||||||
docker compose up -d collector psql-app grafana whois
|
docker compose up -d collector psql-app grafana whois
|
||||||
log " core up"
|
ok "core up"
|
||||||
log "Stage 3/3 - feeders${AUTH_MODE:+ + auth if authelia} (${profiles[*]}) ..."
|
log "Stage 3/3 - feeders${AUTH_MODE:+ + auth if authelia} (${profiles[*]}) ..."
|
||||||
docker compose "${profiles[@]}" up -d
|
docker compose "${profiles[@]}" up -d
|
||||||
log " feeders up"
|
ok "feeders up"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 12. verify -------------------------------------------------------------
|
# --- 12. verify -------------------------------------------------------------
|
||||||
@ -701,6 +858,13 @@ docker logs obmp-collector --tail 8 2>&1 | sed 's/^/ /' || true
|
|||||||
# --- 13. post-deploy notes --------------------------------------------------
|
# --- 13. post-deploy notes --------------------------------------------------
|
||||||
echo
|
echo
|
||||||
printf '\033[1;32m[deploy] done\033[0m\n'
|
printf '\033[1;32m[deploy] done\033[0m\n'
|
||||||
|
hr
|
||||||
|
log "Verify (any time):"
|
||||||
|
echo " docker compose ps # every service Up / healthy"
|
||||||
|
echo " docker exec -i obmp-psql psql -U openbmp -d openbmp -c 'SELECT name, state FROM routers;'"
|
||||||
|
echo " (DB schema is created automatically by psql-app on its first run)"
|
||||||
|
log "Tear down later: ./deploy.sh teardown (add --wipe-data for a full wipe)"
|
||||||
|
hr
|
||||||
if [ "$DTYPE" = "remote" ]; then
|
if [ "$DTYPE" = "remote" ]; then
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Remote collector active. BMP ingest -> $CENTRAL_KAFKA (central store).
|
Remote collector active. BMP ingest -> $CENTRAL_KAFKA (central store).
|
||||||
|
|||||||
@ -64,4 +64,4 @@ Storage scales with prefixes: ~1 GB per peer with a full internet table
|
|||||||
|
|
||||||
> All figures are engineering estimates to calibrate against your own
|
> All figures are engineering estimates to calibrate against your own
|
||||||
> watermarking, not measured specs. Replace them with real numbers once a
|
> watermarking, not measured specs. Replace them with real numbers once a
|
||||||
> prod-realistic node has been load-tested (see docs/production-sizing.md).
|
> prod-realistic node has been load-tested (see production-sizing.md).
|
||||||
|
|||||||
56
docs/DOCS.md
56
docs/DOCS.md
@ -1,5 +1,14 @@
|
|||||||
# OpenBMP + ExaBGP Route Injector — Full Documentation
|
# OpenBMP + ExaBGP Route Injector — Full Documentation
|
||||||
|
|
||||||
|
> **LEGACY LAB WALKTHROUGH.** This guide was written against the original
|
||||||
|
> single-host dev lab and keeps its literal values (e.g. host `10.40.40.202`,
|
||||||
|
> NETCONF user `webui`) as *illustrations* — substitute your own `.env`
|
||||||
|
> values (`HOST_IP`, `ROUTER_FACING_IP`/`PORT`, `IOSXR_NETCONF_*`). For
|
||||||
|
> current deployments start instead at:
|
||||||
|
> **[deploy.sh + README quickstart](../README.md)** (operators) and
|
||||||
|
> **[router-integration.md](router-integration.md)** (network engineers).
|
||||||
|
> Where this file disagrees with those, they win.
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
1. [What Is This Project?](#1-what-is-this-project)
|
1. [What Is This Project?](#1-what-is-this-project)
|
||||||
@ -48,7 +57,7 @@ This is a **BGP Monitoring Platform (BMP) lab stack** deployed via Docker Compos
|
|||||||
|
|
||||||
```
|
```
|
||||||
IOS-XR Routers (9x, AS 65020)
|
IOS-XR Routers (9x, AS 65020)
|
||||||
BMP telemetry on TCP 5000
|
BMP telemetry on TCP 1790 (maps to collector :5000 in-container)
|
||||||
|
|
|
|
||||||
v
|
v
|
||||||
obmp-collector (openbmp/collector:2.2.3)
|
obmp-collector (openbmp/collector:2.2.3)
|
||||||
@ -101,7 +110,7 @@ Traffic Generator (Phase 4):
|
|||||||
|-----------|-------|---------|------|
|
|-----------|-------|---------|------|
|
||||||
| obmp-zookeeper | confluentinc/cp-zookeeper:7.1.1 | 2181 (internal) | Kafka coordination |
|
| obmp-zookeeper | confluentinc/cp-zookeeper:7.1.1 | 2181 (internal) | Kafka coordination |
|
||||||
| obmp-kafka | confluentinc/cp-kafka:7.1.1 | 9092 | Message broker |
|
| obmp-kafka | confluentinc/cp-kafka:7.1.1 | 9092 | Message broker |
|
||||||
| obmp-collector | openbmp/collector:2.2.3 | 5000 | BMP receiver |
|
| obmp-collector | openbmp/collector:2.2.3 | 1790 -> 5000 | BMP receiver (external ${ROUTER_FACING_PORT:-1790} maps to in-container 5000) |
|
||||||
| obmp-psql-app | openbmp/psql-app:2.2.2 | 9005 | Kafka→PostgreSQL consumer |
|
| obmp-psql-app | openbmp/psql-app:2.2.2 | 9005 | Kafka→PostgreSQL consumer |
|
||||||
| obmp-psql | openbmp/postgres:2.2.1 | 5432 | TimescaleDB storage |
|
| obmp-psql | openbmp/postgres:2.2.1 | 5432 | TimescaleDB storage |
|
||||||
| obmp-grafana | grafana/grafana:9.1.7 | 3000 | Visualization |
|
| obmp-grafana | grafana/grafana:9.1.7 | 3000 | Visualization |
|
||||||
@ -119,7 +128,7 @@ Traffic Generator (Phase 4):
|
|||||||
|
|
||||||
- Docker Engine (20.10+) and Docker Compose v2
|
- Docker Engine (20.10+) and Docker Compose v2
|
||||||
- Host IP `10.40.40.202` reachable from the CML management network
|
- Host IP `10.40.40.202` reachable from the CML management network
|
||||||
- CML routers with BMP configured pointing to the collector's router-facing address (see docs/router-bmp-config.md)
|
- CML routers with BMP configured pointing to the collector's router-facing address (see router-bmp-config.md)
|
||||||
- CML CORE routers configured with ExaBGP as eBGP neighbor (see Section 5)
|
- CML CORE routers configured with ExaBGP as eBGP neighbor (see Section 5)
|
||||||
- `OBMP_DATA_ROOT` directory created (default: `/var/openbmp`)
|
- `OBMP_DATA_ROOT` directory created (default: `/var/openbmp`)
|
||||||
|
|
||||||
@ -181,15 +190,15 @@ mkdir -p ${OBMP_DATA_ROOT}/grafana/dashboards
|
|||||||
sudo chmod -R 777 $OBMP_DATA_ROOT
|
sudo chmod -R 777 $OBMP_DATA_ROOT
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.3 Initialise the database (first run only)
|
### 4.3 Database initialisation (automatic)
|
||||||
|
|
||||||
Create the init trigger file — this causes psql-app to create all tables on startup:
|
psql-app creates the full schema **automatically on its first run** and then
|
||||||
|
drops a marker file `${OBMP_DATA_ROOT}/config/do_not_init_db` so later
|
||||||
|
restarts skip it. No manual trigger is needed.
|
||||||
|
|
||||||
```bash
|
> To force a schema re-init against an empty database, delete the marker:
|
||||||
touch ${OBMP_DATA_ROOT}/config/init_db
|
> `rm ${OBMP_DATA_ROOT}/config/do_not_init_db` and restart `obmp-psql-app`.
|
||||||
```
|
> Never do this against a database whose data you want to keep.
|
||||||
|
|
||||||
> **Warning:** Do not create this file on subsequent runs unless you want to wipe and recreate the entire database.
|
|
||||||
|
|
||||||
### 4.4 Copy Grafana provisioning files
|
### 4.4 Copy Grafana provisioning files
|
||||||
|
|
||||||
@ -554,7 +563,7 @@ Default credentials: `admin` / `openbmp` (anonymous access also enabled)
|
|||||||
|
|
||||||
> History dashboards require `ip_rib_log` and `stats_chg_*` table data. Run `inject.py churn` to populate these.
|
> History dashboards require `ip_rib_log` and `stats_chg_*` table data. Run `inject.py churn` to populate these.
|
||||||
|
|
||||||
### OBMP-Learning Dashboards (folder: `OBMP-Learning`)
|
### OBMP-Learning Dashboards (folder: `OBMP-Reference`)
|
||||||
|
|
||||||
Six learning-focused dashboards in a separate folder, designed to teach BGP concepts using live lab data.
|
Six learning-focused dashboards in a separate folder, designed to teach BGP concepts using live lab data.
|
||||||
|
|
||||||
@ -569,7 +578,7 @@ Six learning-focused dashboards in a separate folder, designed to teach BGP conc
|
|||||||
|
|
||||||
> **RPKI note:** The `rpki_validator` table is populated by a cron job in `psql-app` every 2 hours. Dashboard `obmp-learn-04` will show zero counts until the cron runs — check `ENABLE_RPKI=1` in `docker-compose.yml`.
|
> **RPKI note:** The `rpki_validator` table is populated by a cron job in `psql-app` every 2 hours. Dashboard `obmp-learn-04` will show zero counts until the cron runs — check `ENABLE_RPKI=1` in `docker-compose.yml`.
|
||||||
|
|
||||||
### Advanced Analytics Dashboards (folder: `OBMP-Learning`)
|
### Advanced Analytics Dashboards (folder: `OBMP-Reference`)
|
||||||
|
|
||||||
Four advanced dashboards that go beyond basic BMP monitoring, unlocking TE/SR data and providing heuristic analysis.
|
Four advanced dashboards that go beyond basic BMP monitoring, unlocking TE/SR data and providing heuristic analysis.
|
||||||
|
|
||||||
@ -584,7 +593,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 `docs/DB_SCHEMA.md`. 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 `DB_SCHEMA.md`. It documents all 33 tables, 11 views, TE/SR columns, enum types, and common query patterns.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -653,7 +662,7 @@ Should show topics like `openbmp.parsed.unicast_prefix`, `openbmp.parsed.peer`,
|
|||||||
|
|
||||||
### 9.6 Grafana datasource
|
### 9.6 Grafana datasource
|
||||||
|
|
||||||
Open `http://10.40.40.202:3000` → Configuration → Data Sources → OpenBMP → Test.
|
Open `http://${HOST_IP}:3000` → Configuration → Data Sources → PostgreSQL → Test.
|
||||||
Should return "Database Connection OK".
|
Should return "Database Connection OK".
|
||||||
|
|
||||||
### 9.7 BMP collector receiving data
|
### 9.7 BMP collector receiving data
|
||||||
@ -822,7 +831,7 @@ Verify: `show bgp 1.1.1.0/24` — should show `Status: s (active), bestpath`.
|
|||||||
|
|
||||||
### Grafana shows no data
|
### Grafana shows no data
|
||||||
|
|
||||||
1. Check datasource: Configuration → Data Sources → OpenBMP → Test
|
1. Check datasource: Configuration → Data Sources → PostgreSQL → Test
|
||||||
2. Verify psql-app is writing: `docker compose -p obmp logs psql-app`
|
2. Verify psql-app is writing: `docker compose -p obmp logs psql-app`
|
||||||
3. Check the database directly (see database queries above)
|
3. Check the database directly (see database queries above)
|
||||||
4. History dashboards need route churn — run `python3 inject.py churn`
|
4. History dashboards need route churn — run `python3 inject.py churn`
|
||||||
@ -881,9 +890,7 @@ Adjust in `docker-compose.yml` under the `psql-app` service environment block.
|
|||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `EXABGP_LOCAL_IP` | `10.40.40.202` | Host IP ExaBGP binds to and uses as router-id |
|
| `EXABGP_LOCAL_IP` | `10.40.40.202` | Host IP ExaBGP binds to and uses as router-id |
|
||||||
| `EXABGP_LOCAL_AS` | `65100` | ExaBGP's AS number |
|
| `EXABGP_LOCAL_AS` | `65100` | ExaBGP's AS number |
|
||||||
| `EXABGP_PEER_AS` | `65020` | AS of the IOS-XR lab |
|
| `EXABGP_PEERS` | (semicolon list) | Peers as `ip:peer_as:description;...` — replaces the retired `EXABGP_PEER_AS`/`EXABGP_PEER_1`/`EXABGP_PEER_2` |
|
||||||
| `EXABGP_PEER_1` | `10.100.0.100` | First CORE router to peer with |
|
|
||||||
| `EXABGP_PEER_2` | `10.100.0.200` | Second CORE router to peer with |
|
|
||||||
| `EXABGP_API_PORT` | `5050` | Flask API port |
|
| `EXABGP_API_PORT` | `5050` | Flask API port |
|
||||||
|
|
||||||
### psql-app container (key variables)
|
### psql-app container (key variables)
|
||||||
@ -955,12 +962,13 @@ Expected: gRPC listening on port 57400.
|
|||||||
|
|
||||||
### Telemetry Data Collected
|
### Telemetry Data Collected
|
||||||
|
|
||||||
Telegraf subscribes to two IOS-XR YANG paths at 10-second intervals:
|
Telegraf subscribes to two OpenConfig paths (see telegraf/telegraf.conf,
|
||||||
|
the source of truth):
|
||||||
|
|
||||||
| Subscription | YANG Path | Data |
|
| Subscription | OpenConfig path | Interval | Data |
|
||||||
|-------------|-----------|------|
|
|-------------|-----------------|----------|------|
|
||||||
| interface_counters | `Cisco-IOS-XR-infra-statsd-oper:infra-statistics/interfaces/interface/latest/generic-counters` | bytes/packets in/out, errors, drops, CRC |
|
| interface_counters | `openconfig-interfaces:/interfaces/interface/state/counters` | 10s | bytes/packets in/out, errors, drops |
|
||||||
| interface_rates | `Cisco-IOS-XR-infra-statsd-oper:infra-statistics/interfaces/interface/latest/data-rate` | bits/sec in/out, packet rate |
|
| interface_state | `openconfig-interfaces:/interfaces/interface/state` | 30s | oper status, MTU, speed |
|
||||||
|
|
||||||
### InfluxDB Access
|
### InfluxDB Access
|
||||||
|
|
||||||
@ -1110,6 +1118,6 @@ The **Combined BMP + Telemetry** dashboard shows both control-plane (BMP BGP upd
|
|||||||
|
|
||||||
| Variable | Default | Description |
|
| Variable | Default | Description |
|
||||||
|----------|---------|-------------|
|
|----------|---------|-------------|
|
||||||
| `TRAFFIC_GEN_API_PORT` | `5051` | Flask API listen port |
|
| `TRAFFIC_GEN_PORT` | `5051` | Flask API listen port |
|
||||||
| `TRAFFIC_GEN_MODE` | `sender` | Operating mode: `sender` or `responder` |
|
| `TRAFFIC_GEN_MODE` | `sender` | Operating mode: `sender` or `responder` |
|
||||||
| `INFLUXDB_TOKEN` | `openbmp-telemetry-token` | InfluxDB auth token (Telegraf) |
|
| `INFLUXDB_TOKEN` | `openbmp-telemetry-token` | InfluxDB auth token (Telegraf) |
|
||||||
|
|||||||
@ -41,7 +41,7 @@ feeders.
|
|||||||
## 6. Size for BMP burst, not steady state
|
## 6. Size for BMP burst, not steady state
|
||||||
Generic docs say the collector is light — true, and misleading: the store
|
Generic docs say the collector is light — true, and misleading: the store
|
||||||
saturates under burst (full table x monitored sessions, Postgres write
|
saturates under burst (full table x monitored sessions, Postgres write
|
||||||
amplification). See docs/DEPLOYMENT-TYPES.md for the full model.
|
amplification). See DEPLOYMENT-TYPES.md for the full model.
|
||||||
|
|
||||||
## 7. Ports 5000 and 3000 are crowded
|
## 7. Ports 5000 and 3000 are crowded
|
||||||
The collector's 5000 and Grafana's 3000 are commonly already occupied.
|
The collector's 5000 and Grafana's 3000 are commonly already occupied.
|
||||||
@ -100,4 +100,4 @@ setup.sh renders the same port into gobgp's BMP export (gobgp is
|
|||||||
host-networked and must hit the *published* port), and the cml tooling reads
|
host-networked and must hit the *published* port), and the cml tooling reads
|
||||||
`ROUTER_FACING_IP`/`ROUTER_FACING_PORT` from `.env`. Remaining hands-on step:
|
`ROUTER_FACING_IP`/`ROUTER_FACING_PORT` from `.env`. Remaining hands-on step:
|
||||||
re-apply the config to routers still pointed at 5000. See
|
re-apply the config to routers still pointed at 5000. See
|
||||||
docs/router-bmp-config.md.
|
router-bmp-config.md.
|
||||||
|
|||||||
32
docs/README.md
Normal file
32
docs/README.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# Documentation index
|
||||||
|
|
||||||
|
Two tracks depending on who you are. Docs higher in each list are the entry
|
||||||
|
points; the rest are references you'll be routed to when needed.
|
||||||
|
|
||||||
|
## Operators — deploying and running the stack
|
||||||
|
|
||||||
|
| Doc | What it covers |
|
||||||
|
|---|---|
|
||||||
|
| [../README.md](../README.md) | Quickstart: clone, pin the checkout, run `deploy.sh` |
|
||||||
|
| [DEPLOYMENT-TYPES.md](DEPLOYMENT-TYPES.md) | The three `--scope` types (full-stack / remote / central-store) + burst sizing model |
|
||||||
|
| [PORTABILITY-FINDINGS.md](PORTABILITY-FINDINGS.md) | Every greenfield trap and how the tooling handles it — read before deploying to a new host |
|
||||||
|
| [production-sizing.md](production-sizing.md) | Memory limits and host sizing for production |
|
||||||
|
| [backup-restore.md](backup-restore.md) | Postgres backup/restore (`scripts/pg-backup.sh`) |
|
||||||
|
| [security-hardening.md](security-hardening.md) | Hardening checklist for exposed deployments |
|
||||||
|
| [DB_SCHEMA.md](DB_SCHEMA.md) | Full database schema reference (33 tables, views, query patterns) |
|
||||||
|
| [ROADMAP.md](ROADMAP.md) | Longer-term work items |
|
||||||
|
|
||||||
|
## Network engineers — feeding routers into the stack
|
||||||
|
|
||||||
|
| Doc | What it covers |
|
||||||
|
|---|---|
|
||||||
|
| [router-integration.md](router-integration.md) | **Start here.** The four ingest paths (BMP, BGP-LS, gNMI, NETCONF), bring-up order, verification |
|
||||||
|
| [router-bmp-config.md](router-bmp-config.md) | BMP deep-dive: address/port selection (WSL vs native), activation scope, troubleshooting |
|
||||||
|
| [../router-blueprints/](../router-blueprints/) | Copy-paste IOS-XR fragments with `<PLACEHOLDER>` substitutions |
|
||||||
|
|
||||||
|
## Legacy
|
||||||
|
|
||||||
|
| Doc | Status |
|
||||||
|
|---|---|
|
||||||
|
| [DOCS.md](DOCS.md) | Original single-host dev-lab walkthrough. Values are illustrative; where it disagrees with the docs above, they win. |
|
||||||
|
| [handoff/](handoff/) | Point-in-time session handoff packets, kept for the record. |
|
||||||
@ -96,9 +96,10 @@ Replace hardcoded IPs in `docker-compose.yml` (Kafka listener, ExaBGP env vars).
|
|||||||
|
|
||||||
Replace hardcoded gNMI addresses in `telegraf/telegraf.conf` with env var substitution. Pass `GNMI_TARGETS` from docker-compose.yml.
|
Replace hardcoded gNMI addresses in `telegraf/telegraf.conf` with env var substitution. Pass `GNMI_TARGETS` from docker-compose.yml.
|
||||||
|
|
||||||
### A6. Fix InfluxDB datasource URL
|
### A6. Fix InfluxDB datasource URL — DONE
|
||||||
|
|
||||||
`obmp-grafana/provisioning/datasources/influxdb-ds.yml`: replace `http://10.40.40.202:8086` with `http://obmp-influxdb:8086`.
|
Completed: `obmp-grafana/provisioning/datasources/influxdb-ds.yml` now reads
|
||||||
|
`http://obmp-influxdb:8086`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -115,8 +115,9 @@ EOSQL
|
|||||||
```
|
```
|
||||||
|
|
||||||
> Restoring into a **brand-new container**? Bring `obmp-psql` up first and let
|
> Restoring into a **brand-new container**? Bring `obmp-psql` up first and let
|
||||||
> it initialize, but **do not** create the `config/init_db` trigger file —
|
> it initialize, and create the skip-marker BEFORE psql-app's first start —
|
||||||
> the schema comes from the dump, not from psql-app's first-run migration.
|
> `touch ${OBMP_DATA_ROOT}/config/do_not_init_db` — so psql-app does not run
|
||||||
|
> its first-run schema migration; the schema comes from the dump instead.
|
||||||
|
|
||||||
### 3. Restore the dump
|
### 3. Restore the dump
|
||||||
|
|
||||||
|
|||||||
@ -44,7 +44,7 @@ bmp server 1 initial-refresh delay 60 spread 30
|
|||||||
|
|
||||||
The `initial-refresh delay/spread` staggering matters at scale: it spreads
|
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
|
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).
|
burst that sizes the whole store tier (DEPLOYMENT-TYPES.md).
|
||||||
|
|
||||||
## Activating monitoring on BGP sessions
|
## Activating monitoring on BGP sessions
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ BMP only reports sessions that are `bmp-activate`d:
|
|||||||
|
|
||||||
```
|
```
|
||||||
router bgp <ASN>
|
router bgp <ASN>
|
||||||
neighbor-group RR-CLIENTS
|
neighbor-group RR-CLIENT
|
||||||
bmp-activate server 1
|
bmp-activate server 1
|
||||||
!
|
!
|
||||||
!
|
!
|
||||||
|
|||||||
@ -81,9 +81,10 @@ it before configuring. Covers:
|
|||||||
`HOST_IP`** on WSL deployments — the WSL VM's NAT address is
|
`HOST_IP`** on WSL deployments — the WSL VM's NAT address is
|
||||||
unreachable from real routers; see
|
unreachable from real routers; see
|
||||||
[PORTABILITY-FINDINGS.md](PORTABILITY-FINDINGS.md) finding 4).
|
[PORTABILITY-FINDINGS.md](PORTABILITY-FINDINGS.md) finding 4).
|
||||||
- Which port (`ROUTER_FACING_PORT`, defaults to 1790 — the IANA BMP port;
|
- Which port (`ROUTER_FACING_PORT` — 1790, the IANA BMP port, standardized
|
||||||
existing lab routers configured for 5000 need `--router-port 5000`
|
2026-07; the legacy 5000 is retired. Routers still configured for 5000
|
||||||
until reconfigured).
|
must be re-applied — `cml/proxmox_bmp_config.py` pushes the current
|
||||||
|
`.env` values).
|
||||||
- The BMP `bmp server 1` block, flat formal form.
|
- The BMP `bmp server 1` block, flat formal form.
|
||||||
- Activation via `neighbor-group BMP-MONITORED`.
|
- Activation via `neighbor-group BMP-MONITORED`.
|
||||||
- **RR-scope-only activation** for load reduction — the single biggest
|
- **RR-scope-only activation** for load reduction — the single biggest
|
||||||
|
|||||||
@ -5,7 +5,7 @@ present for the OpenBMP Docker stack to ingest data from it. Copy, edit
|
|||||||
the `<PLACEHOLDER>` values for your topology, paste.
|
the `<PLACEHOLDER>` values for your topology, paste.
|
||||||
|
|
||||||
For the walkthrough (what each ingest path does, in what order to apply,
|
For the walkthrough (what each ingest path does, in what order to apply,
|
||||||
how to verify), see [docs/router-config-guide.md](../docs/router-config-guide.md).
|
how to verify), see [docs/router-integration.md](../docs/router-integration.md).
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
|
|||||||
@ -62,4 +62,5 @@ router bgp <LOCAL_ASN>
|
|||||||
!
|
!
|
||||||
! On the stack side:
|
! On the stack side:
|
||||||
! docker logs obmp-collector 2>&1 | grep -i "peer up"
|
! docker logs obmp-collector 2>&1 | grep -i "peer up"
|
||||||
! Grafana -> OBMP-Operations -> Base-1001 -> Peers Table
|
! Grafana -> OBMP-Operations -> Inventory (router appears, connected)
|
||||||
|
! Grafana -> OBMP-Operations -> Peer Detail (per-session state + prefixes)
|
||||||
|
|||||||
@ -32,6 +32,21 @@ router bgp <LOCAL_ASN>
|
|||||||
bmp-activate server 1
|
bmp-activate server 1
|
||||||
address-family ipv4 unicast
|
address-family ipv4 unicast
|
||||||
route-reflector-client
|
route-reflector-client
|
||||||
|
!
|
||||||
|
! *** NEXT-HOP-SELF CAVEAT *********************************************
|
||||||
|
! next-hop-self here ONLY rewrites the next-hop of eBGP-learned and
|
||||||
|
! locally-originated routes advertised to clients. That is exactly what
|
||||||
|
! this lab wants: the full table enters at the cores via eBGP (GoBGP/
|
||||||
|
! ExaBGP) and clients must resolve those routes via the RR's loopback.
|
||||||
|
!
|
||||||
|
! IOS-XR will NOT rewrite the next-hop of iBGP-REFLECTED routes unless
|
||||||
|
! you ALSO configure:
|
||||||
|
! router bgp <LOCAL_ASN>
|
||||||
|
! ibgp policy out enforce-modifications
|
||||||
|
! On a pure route reflector with no eBGP feed, this line is a silent
|
||||||
|
! no-op and clients must resolve next-hops via the IGP instead. Do not
|
||||||
|
! copy it into such a design expecting reflected-route NH rewrite.
|
||||||
|
! **********************************************************************
|
||||||
next-hop-self
|
next-hop-self
|
||||||
!
|
!
|
||||||
address-family link-state link-state
|
address-family link-state link-state
|
||||||
@ -54,4 +69,4 @@ router bgp <LOCAL_ASN>
|
|||||||
! show bgp neighbors <CLIENT_LOOPBACK> advertised-routes | count
|
! show bgp neighbors <CLIENT_LOOPBACK> advertised-routes | count
|
||||||
!
|
!
|
||||||
! On the stack side (once the routers are BMP-reporting and reflecting):
|
! On the stack side (once the routers are BMP-reporting and reflecting):
|
||||||
! Grafana -> OBMP-Learning -> RR Loc-RIB Diff (should populate)
|
! Grafana -> OBMP-Reference -> RR Loc-RIB Diff (should populate)
|
||||||
|
|||||||
90
scripts/deploy-lib.sh
Normal file
90
scripts/deploy-lib.sh
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# deploy-lib.sh - shared helpers for deploy.sh and setup.sh.
|
||||||
|
#
|
||||||
|
# Both scripts source this file so output styling and .env handling stay
|
||||||
|
# identical and are defined exactly once. Safe to source multiple times.
|
||||||
|
#
|
||||||
|
# Provides:
|
||||||
|
# log / ok / warn / die / hr - consistent colored output
|
||||||
|
# banner "title" - boxed section banner
|
||||||
|
# step N M "title" - numbered decision/stage header
|
||||||
|
# ask VAR "prompt" "default" - editable-default prompt (honors ARG_YES)
|
||||||
|
# confirm "prompt" - y/N confirm (honors ARG_YES)
|
||||||
|
# get_env KEY / set_env KEY VAL - .env read/write without sourcing it
|
||||||
|
# is_ipv4 ADDR - dotted-quad shape check
|
||||||
|
#
|
||||||
|
# Conventions: callers may set ARG_YES=1 for unattended runs and TAG to
|
||||||
|
# change the log prefix (defaults to "deploy").
|
||||||
|
|
||||||
|
TAG="${TAG:-deploy}"
|
||||||
|
ARG_YES="${ARG_YES:-0}"
|
||||||
|
|
||||||
|
log() { printf '\033[1;36m[%s]\033[0m %s\n' "$TAG" "$*"; }
|
||||||
|
ok() { printf '\033[1;32m[%s] \xe2\x9c\x94\033[0m %s\n' "$TAG" "$*"; }
|
||||||
|
warn() { printf '\033[1;33m[%s][warn]\033[0m %s\n' "$TAG" "$*" >&2; }
|
||||||
|
die() { printf '\033[1;31m[%s][err]\033[0m %s\n' "$TAG" "$*" >&2; exit 1; }
|
||||||
|
hr() { printf '\033[2m%s\033[0m\n' "----------------------------------------------------------------"; }
|
||||||
|
|
||||||
|
# Boxed banner, e.g.:
|
||||||
|
# +--------------------------------------------------------------+
|
||||||
|
# | OpenBMP stack deploy |
|
||||||
|
# +--------------------------------------------------------------+
|
||||||
|
banner() {
|
||||||
|
local title="$1" width=62
|
||||||
|
printf '\033[1;36m+%s+\n' "$(printf -- '-%.0s' $(seq 1 $width))"
|
||||||
|
printf '| %-*s|\n' "$((width - 2))" "$title"
|
||||||
|
printf '+%s+\033[0m\n' "$(printf -- '-%.0s' $(seq 1 $width))"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Numbered header for decisions and stages: step 2 5 "Deployment type"
|
||||||
|
step() {
|
||||||
|
local n="$1" total="$2" title="$3"
|
||||||
|
printf '\n\033[1;36m[%s] Step %s/%s\033[0m \033[1m%s\033[0m\n' "$TAG" "$n" "$total" "$title"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prompt with an editable default. Honors ARG_YES (accepts default silently).
|
||||||
|
ask() {
|
||||||
|
local __var="$1" __prompt="$2" __default="${3:-}" __reply=""
|
||||||
|
if [ "$ARG_YES" -eq 1 ]; then
|
||||||
|
printf -v "$__var" '%s' "$__default"; return
|
||||||
|
fi
|
||||||
|
if [ -n "$__default" ]; then
|
||||||
|
read -r -p "$__prompt [$__default]: " __reply
|
||||||
|
__reply="${__reply:-$__default}"
|
||||||
|
else
|
||||||
|
read -r -p "$__prompt: " __reply
|
||||||
|
fi
|
||||||
|
printf -v "$__var" '%s' "$__reply"
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm() {
|
||||||
|
local __prompt="$1" __reply=""
|
||||||
|
[ "$ARG_YES" -eq 1 ] && return 0
|
||||||
|
read -r -p "$__prompt [y/N]: " __reply
|
||||||
|
[ "$__reply" = "y" ] || [ "$__reply" = "Y" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
# Read a single KEY=value from .env without sourcing it -- .env contains keys
|
||||||
|
# with hyphens (PROX-CML_*) that a shell `source` would choke on.
|
||||||
|
get_env() { grep -E "^$1=" .env | head -1 | cut -d= -f2- || true; }
|
||||||
|
|
||||||
|
# Set KEY=value in .env: replace the line if present, else append.
|
||||||
|
set_env() {
|
||||||
|
local key="$1" val="$2"
|
||||||
|
if grep -qE "^${key}=" .env; then
|
||||||
|
# `|` delimiter -- values are hex/IPs, no `|`.
|
||||||
|
sed -i "s|^${key}=.*|${key}=${val}|" .env
|
||||||
|
else
|
||||||
|
printf '%s=%s\n' "$key" "$val" >> .env
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dotted-quad shape check (rejects placeholders like <WINDOWS_LAN_IP>).
|
||||||
|
is_ipv4() {
|
||||||
|
local ip="$1" o
|
||||||
|
[[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]] || return 1
|
||||||
|
IFS=. read -r -a o <<<"$ip"
|
||||||
|
for x in "${o[@]}"; do [ "$x" -le 255 ] || return 1; done
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@ -20,14 +20,19 @@
|
|||||||
.PARAMETER WslIp
|
.PARAMETER WslIp
|
||||||
WSL address to forward to. Default: auto-detected via 'wsl hostname -I'.
|
WSL address to forward to. Default: auto-detected via 'wsl hostname -I'.
|
||||||
|
|
||||||
|
.PARAMETER Remove
|
||||||
|
Teardown mode: delete the portproxy + firewall rules this script creates
|
||||||
|
(pair with './deploy.sh teardown' on the Linux side).
|
||||||
|
|
||||||
.EXAMPLE
|
.EXAMPLE
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\wsl-portproxy.ps1
|
powershell -ExecutionPolicy Bypass -File scripts\wsl-portproxy.ps1
|
||||||
powershell -ExecutionPolicy Bypass -File scripts\wsl-portproxy.ps1 -RouterPort 5000
|
powershell -ExecutionPolicy Bypass -File scripts\wsl-portproxy.ps1 -Remove
|
||||||
#>
|
#>
|
||||||
param(
|
param(
|
||||||
[int]$RouterPort = 1790,
|
[int]$RouterPort = 1790,
|
||||||
[int]$KafkaPort = 9092,
|
[int]$KafkaPort = 9092,
|
||||||
[string]$WslIp = ""
|
[string]$WslIp = "",
|
||||||
|
[switch]$Remove
|
||||||
)
|
)
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
@ -39,6 +44,24 @@ if (-not (New-Object System.Security.Principal.WindowsPrincipal($id)).IsInRole(
|
|||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# -Remove: teardown mode. Deletes the portproxy + firewall rules this script
|
||||||
|
# creates (used by './deploy.sh teardown' guidance). No WSL IP needed.
|
||||||
|
if ($Remove) {
|
||||||
|
foreach ($listen in @($RouterPort, $KafkaPort)) {
|
||||||
|
netsh interface portproxy delete v4tov4 listenport=$listen listenaddress=0.0.0.0 2>$null | Out-Null
|
||||||
|
Write-Host "portproxy rule removed: 0.0.0.0:$listen"
|
||||||
|
$ruleName = "OpenBMP $listen"
|
||||||
|
if (Get-NetFirewallRule -DisplayName $ruleName -ErrorAction SilentlyContinue) {
|
||||||
|
Remove-NetFirewallRule -DisplayName $ruleName
|
||||||
|
Write-Host "firewall rule removed: $ruleName"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "Remaining portproxy rules:"
|
||||||
|
netsh interface portproxy show v4tov4
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
if (-not $WslIp) {
|
if (-not $WslIp) {
|
||||||
$WslIp = (wsl hostname -I).Trim().Split(" ")[0]
|
$WslIp = (wsl hostname -I).Trim().Split(" ")[0]
|
||||||
if (-not $WslIp) { Write-Error "Could not auto-detect the WSL IP; pass -WslIp."; exit 1 }
|
if (-not $WslIp) { Write-Error "Could not auto-detect the WSL IP; pass -WslIp."; exit 1 }
|
||||||
|
|||||||
18
setup.sh
18
setup.sh
@ -27,20 +27,10 @@ if [ ! -f .env ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Read a single KEY=value from .env without sourcing it — .env contains keys
|
# Shared helpers (log ok warn die hr get_env set_env ...) — same styling as
|
||||||
# with hyphens (PROX-CML_*) that a shell `source` would choke on.
|
# deploy.sh, defined once in scripts/deploy-lib.sh.
|
||||||
get_env() { grep -E "^$1=" .env | head -1 | cut -d= -f2- || true; }
|
TAG=setup
|
||||||
|
. "$(dirname "$0")/scripts/deploy-lib.sh"
|
||||||
# Set KEY=value in .env: replace the line if present, else append.
|
|
||||||
set_env() {
|
|
||||||
local key="$1" val="$2"
|
|
||||||
if grep -qE "^${key}=" .env; then
|
|
||||||
# `|` delimiter — values are hex, no `|`.
|
|
||||||
sed -i "s|^${key}=.*|${key}=${val}|" .env
|
|
||||||
else
|
|
||||||
printf '%s=%s\n' "$key" "$val" >> .env
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
OBMP_DATA_ROOT="$(get_env OBMP_DATA_ROOT)"
|
OBMP_DATA_ROOT="$(get_env OBMP_DATA_ROOT)"
|
||||||
OBMP_DATA_ROOT="${OBMP_DATA_ROOT:-/var/openbmp}"
|
OBMP_DATA_ROOT="${OBMP_DATA_ROOT:-/var/openbmp}"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user