Compare commits

...

10 Commits

Author SHA1 Message Date
sam
233dadbb41 Add ExaBGP route injector, Grafana dashboards, and full documentation
- Add exabgp/ container: ExaBGP 5.x + Flask REST API for on-demand BGP
  route injection into CML IOS-XR lab (AS 65020 via eBGP from AS 65100)
- Add 6 injection scenarios: internet_sample, churn, blackhole, anycast,
  full_table, lab_prefixes
- Add inject.py CLI wrapper for the ExaBGP API
- Add iosxr_bgp_config.md with IOS-XR neighbor config and NETCONF script
- Add obmp-grafana/ dashboards and provisioning (17 dashboards)
- Update docker-compose.yml: add exabgp service, fix Kafka external
  listener IP, extend log retention from 90min to 720min
- Add DOCS.md: full project documentation including architecture, setup,
  user guide, sanity checks, troubleshooting, and command reference
- Update .gitignore: exclude .env and .claude/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 14:46:37 -07:00
Tim Evens
3f38af5312 Version 2.2.3 updates
* collector v2.2.3
* collector using debian-stable-slim
* dev-image updated to use debian-stable-slim
* Upgraded librdkafka to v1.9.2
* Fixed permission problems with postgres
* Grafana upgraded to 9.1.7
* psql-app v2.2.2
* postgres updated to use timescaledb-ha:pg14-ts2.8
2022-10-20 07:12:08 -07:00
Tim Evens
0f3312a719 Updates for v2.2.1 2022-06-17 18:20:05 -07:00
Tim Evens
6e616efe10 Updates for 2.2.0
* Use timescaleDB CE intead of OSS
* Have psql-app wait for psql to startup during init db
* Add version file to postgres container
2022-06-12 11:04:59 -07:00
Tim Evens
e19e5ac73a Fix psql container rm file issue 2022-06-10 12:53:24 -07:00
Tim Evens
c1dd8fc15c Add db-import and update irr cron jobs in psql-app container 2022-06-10 06:47:01 -07:00
Tim Evens
f7f13db676 Update logging files for cron jobs 2022-06-08 16:53:03 -07:00
Tim Evens
237345b476 Add ENABLE_DBIP to ``psql-app`` container to auto import DB-IP geo data 2022-06-08 14:53:55 -07:00
Tim Evens
a50c553f66 Version 2.2.0 schema upgrade 2022-06-08 11:54:17 -07:00
Tim Evens
84bec5293b version 2.2.0 updates 2022-06-08 11:53:55 -07:00
48 changed files with 13754 additions and 98 deletions

2
.gitignore vendored
View File

@ -1,3 +1,5 @@
.idea .idea
*.log *.log
.env
.claude/

746
DOCS.md Normal file
View File

@ -0,0 +1,746 @@
# OpenBMP + ExaBGP Route Injector — Full Documentation
## Table of Contents
1. [What Is This Project?](#1-what-is-this-project)
2. [Architecture](#2-architecture)
3. [Prerequisites](#3-prerequisites)
4. [Initial Setup (First Time)](#4-initial-setup-first-time)
5. [IOS-XR Router Configuration](#5-ios-xr-router-configuration)
6. [Starting and Stopping](#6-starting-and-stopping)
7. [Route Injection User Guide](#7-route-injection-user-guide)
8. [Grafana Dashboards](#8-grafana-dashboards)
9. [Sanity Checks](#9-sanity-checks)
10. [Relevant Commands Reference](#10-relevant-commands-reference)
11. [Troubleshooting](#11-troubleshooting)
12. [Data Retention](#12-data-retention)
13. [Environment Variables Reference](#13-environment-variables-reference)
---
## 1. What Is This Project?
This is a **BGP Monitoring Platform (BMP) lab stack** deployed via Docker Compose. It collects, stores, and visualizes BGP routing data from a Cisco IOS-XR lab network (running in Cisco Modeling Labs / CML).
**What it does:**
- Receives BMP (BGP Monitoring Protocol, RFC 7854) telemetry from routers on TCP port 5000
- Streams BMP data through Kafka into a TimescaleDB/PostgreSQL database
- Provides 17 Grafana dashboards for real-time and historical BGP analysis
- Includes an **ExaBGP route injector** that peers with the two CORE routers and injects synthetic BGP routes, enabling testing of BGP policy, route propagation, and Grafana dashboards without needing internet connectivity
**The lab network:**
- AS 65020 — 9 Cisco IOS-XR routers in CML (iBGP full mesh via route-reflectors)
- AS 65100 — ExaBGP container (eBGP peer to both CORE routers)
- CORE-01: `10.100.0.100` (CML-R9K-CORE-01)
- CORE-02: `10.100.0.200` (CML-R9K-CORE-02)
- Host IP: `10.40.40.202` (ExaBGP binds here; reachable from CML management network)
---
## 2. Architecture
```
IOS-XR Routers (9x, AS 65020)
BMP telemetry on TCP 5000
|
v
obmp-collector (openbmp/collector:2.2.3)
|
v
obmp-kafka (confluentinc/cp-kafka:7.1.1)
+ obmp-zookeeper (confluentinc/cp-zookeeper:7.1.1)
|
v
obmp-psql-app (openbmp/psql-app:2.2.2)
Java consumer — writes parsed BGP data to PostgreSQL
|
v
obmp-psql (openbmp/postgres:2.2.1)
PostgreSQL 14 + TimescaleDB
|
+---------> obmp-grafana (grafana/grafana:9.1.7) :3000
| 17 dashboards, PostgreSQL datasource
+---------> obmp-whois (openbmp/whois:2.2.0) :4300
WHOIS query server backed by the DB
ExaBGP (obmp-exabgp, built locally)
python:3.11-slim + exabgp 5.x + Flask API
Peers eBGP to CORE-01 and CORE-02 (AS 65100 -> AS 65020)
HTTP API on :5050 — inject/withdraw routes on demand
Routes propagate via iBGP mesh to all 9 routers -> BMP -> DB -> Grafana
```
### Container Summary
| Container | Image | Port(s) | Role |
|-----------|-------|---------|------|
| obmp-zookeeper | confluentinc/cp-zookeeper:7.1.1 | 2181 (internal) | Kafka coordination |
| obmp-kafka | confluentinc/cp-kafka:7.1.1 | 9092 | Message broker |
| obmp-collector | openbmp/collector:2.2.3 | 5000 | BMP receiver |
| obmp-psql-app | openbmp/psql-app:2.2.2 | 9005 | Kafka→PostgreSQL consumer |
| obmp-psql | openbmp/postgres:2.2.1 | 5432 | TimescaleDB storage |
| obmp-grafana | grafana/grafana:9.1.7 | 3000 | Visualization |
| obmp-whois | openbmp/whois:2.2.0 | 4300 | WHOIS query server |
| obmp-exabgp | local build | 5050 (host net) | BGP route injector |
---
## 3. Prerequisites
- Docker Engine (20.10+) and Docker Compose v2
- Host IP `10.40.40.202` reachable from the CML management network
- CML routers with BMP configured pointing to `10.40.40.202:5000`
- CML CORE routers configured with ExaBGP as eBGP neighbor (see Section 5)
- `OBMP_DATA_ROOT` directory created (default: `/var/openbmp`)
---
## 4. Initial Setup (First Time)
### 4.1 Clone the repository
```bash
git clone <this-repo-url>
cd obmp-docker
```
### 4.2 Create persistent data directories
```bash
export OBMP_DATA_ROOT=/var/openbmp
sudo mkdir -p $OBMP_DATA_ROOT
mkdir -p ${OBMP_DATA_ROOT}/config
mkdir -p ${OBMP_DATA_ROOT}/kafka-data
mkdir -p ${OBMP_DATA_ROOT}/zk-data
mkdir -p ${OBMP_DATA_ROOT}/zk-log
mkdir -p ${OBMP_DATA_ROOT}/postgres/data
mkdir -p ${OBMP_DATA_ROOT}/postgres/ts
mkdir -p ${OBMP_DATA_ROOT}/grafana
mkdir -p ${OBMP_DATA_ROOT}/grafana/dashboards
sudo chmod -R 777 $OBMP_DATA_ROOT
```
### 4.3 Initialise the database (first run only)
Create the init trigger file — this causes psql-app to create all tables on startup:
```bash
touch ${OBMP_DATA_ROOT}/config/init_db
```
> **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
```bash
cp -r obmp-grafana/provisioning ${OBMP_DATA_ROOT}/grafana/
cp -r obmp-grafana/dashboards ${OBMP_DATA_ROOT}/grafana/
```
### 4.5 Start the stack
```bash
OBMP_DATA_ROOT=/var/openbmp docker compose -p obmp up -d
```
Wait ~2 minutes for all services to initialise (especially PostgreSQL and psql-app which run schema migrations).
### 4.6 Verify everything is running
```bash
docker compose -p obmp ps
docker compose -p obmp logs --tail=20 psql-app
```
---
## 5. IOS-XR Router Configuration
The ExaBGP container peers eBGP with both CORE routers. Each CORE router must be configured with:
### 5.1 Route policies (apply once per router)
```
route-policy EXABGP_IN
pass
end-policy
route-policy EXABGP_OUT
drop
end-policy
```
### 5.2 BGP neighbor block
```
router bgp 65020
neighbor 10.40.40.202
remote-as 65100
description ExaBGP-Route-Injector
ebgp-multihop 5
update-source MgmtEth0/RP0/CPU0/0
!
address-family ipv4 unicast
route-policy EXABGP_IN in
route-policy EXABGP_OUT out
next-hop-self
!
!
!
```
### 5.3 Static route for next-hop resolution
IOS-XR BGP does not use the default route (0.0.0.0/0) to resolve BGP next-hops. A more-specific static route for the ExaBGP host subnet is required in the default VRF:
```
router static
address-family ipv4 unicast
10.40.40.0/24 10.100.0.254
!
!
```
### 5.4 Config notes
| Knob | Why |
|------|-----|
| `remote-as 65100` | ExaBGP presents as AS 65100 (eBGP to your AS 65020 mesh) |
| `ebgp-multihop 5` | Host and router are on different subnets |
| `update-source MgmtEth0/RP0/CPU0/0` | ExaBGP is reachable via the management interface |
| `next-hop-self` | Replace ExaBGP's next-hop (10.40.40.202) with the CORE router's address when reflecting into iBGP — ensures all routers can resolve the next-hop |
| `EXABGP_OUT` drops | Prevents the lab from advertising its own prefixes back to ExaBGP |
| Static route | Required: IOS-XR BGP will not install injected routes as bestpaths without a specific route to the next-hop |
### 5.5 NETCONF alternative
See `exabgp/iosxr_bgp_config.md` for a Python/ncclient script that pushes all of the above config programmatically.
Credentials: `username=webui`, `password=cisco`, port 830.
---
## 6. Starting and Stopping
### Start all services
```bash
OBMP_DATA_ROOT=/var/openbmp docker compose -p obmp up -d
```
### Stop all services (preserve data)
```bash
docker compose -p obmp down
```
### Stop and remove all data (full reset)
```bash
docker compose -p obmp down -v
sudo rm -rf /var/openbmp
```
### Rebuild the ExaBGP container (after code changes)
```bash
docker compose -p obmp build exabgp
docker compose -p obmp up -d exabgp
```
### Restart a single service
```bash
docker compose -p obmp restart <service>
# e.g.:
docker compose -p obmp restart exabgp
docker compose -p obmp restart psql-app
```
---
## 7. Route Injection User Guide
The ExaBGP container exposes a Flask REST API on port 5050 (host network). The `inject.py` CLI wraps this API.
### 7.1 Setup
```bash
cd exabgp
pip install requests # only needed if running inject.py from the host
```
### 7.2 Check status
```bash
python3 inject.py status
```
Output shows API health, active route count, and peer states:
```json
{
"status": "ok",
"active_routes": 77,
"peers": {
"10.100.0.100": {"state": "up", "updated": "2026-03-05T10:00:00Z"},
"10.100.0.200": {"state": "up", "updated": "2026-03-05T10:00:00Z"}
}
}
```
### 7.3 List available scenarios
```bash
python3 inject.py scenarios
```
| Scenario | Routes | Description |
|----------|--------|-------------|
| `internet_sample` | ~94 | Partial internet table — real public prefixes, realistic AS paths (Cloudflare, Google, AWS, Azure, etc.) |
| `churn` | 30 | RFC documentation prefixes for announce/withdraw churn testing |
| `blackhole` | 5 | /32 prefixes with RTBH community (65100:666 + 65535:666) |
| `anycast` | 3 | Same prefixes with varying AS paths and MEDs (best-path testing) |
| `full_table` | 500+ | Large partial internet table with synthetic /24s |
| `lab_prefixes` | 8 | Enterprise/SP-style routes with communities and local-pref |
### 7.4 Load a scenario
```bash
python3 inject.py scenario internet_sample
```
Routes propagate: ExaBGP → CORE-01/CORE-02 (eBGP) → all 9 routers (iBGP) → BMP → Kafka → PostgreSQL → Grafana.
### 7.5 Withdraw a scenario
```bash
python3 inject.py withdraw-scenario internet_sample
```
### 7.6 Announce individual prefixes
```bash
python3 inject.py announce 10.0.0.0/8 \
--as-path 65100 3356 15169 \
--community 65100:100 \
--med 100
```
### 7.7 Withdraw individual prefixes
```bash
python3 inject.py withdraw 10.0.0.0/8
```
### 7.8 Withdraw everything
```bash
python3 inject.py withdraw-all
```
### 7.9 Generate route churn (populate history tables)
The `churn` command cycles the churn scenario repeatedly, generating `ip_rib_log` and `stats_chg_*` entries that power Grafana's history dashboards.
```bash
# 5 cycles, 30 seconds apart
python3 inject.py churn --count 5 --interval 30
# Run indefinitely until Ctrl+C
python3 inject.py churn
```
### 7.10 REST API directly (curl)
```bash
BASE=http://localhost:5050
# Health
curl $BASE/healthz
# List scenarios
curl $BASE/scenarios
# Load scenario
curl -X POST $BASE/scenario/internet_sample
# Announce custom prefix
curl -X POST $BASE/announce \
-H 'Content-Type: application/json' \
-d '{"prefixes":["10.0.0.0/8"],"as_path":[65100,3356,15169],"communities":["65100:100"]}'
# Withdraw all
curl -X POST $BASE/withdraw/all
# Peer state
curl $BASE/peers
```
### 7.11 Adding custom scenarios
Edit `exabgp/scenarios/__init__.py`. Add an entry to `SCENARIOS` following the existing pattern:
```python
SCENARIOS['my_scenario'] = {
'description': 'My custom routes',
'routes': [
_r('192.0.2.0/24', [65100, 65200], communities=['65100:100']),
],
}
```
The `scenarios/` directory is volume-mounted into the container, so changes are live without rebuilding. However, the Python module is imported at container start — **restart the container** after editing:
```bash
docker compose -p obmp restart exabgp
```
---
## 8. Grafana Dashboards
Access: `http://10.40.40.202:3000`
Default credentials: `admin` / `openbmp` (anonymous access also enabled)
### Dashboard Categories
| Category | Dashboard | Description |
|----------|-----------|-------------|
| General | OBMP Home | Overview / landing page |
| Base | Inventory | Router and peer inventory |
| Base | Looking Glass | Real-time RIB lookup by prefix |
| Base | ASN View | ASN-level routing view |
| History | Prefix History | Route change history for a prefix |
| History | Prefix History by ASN | Filtered by origin AS |
| History | Prefix History by Community | Filtered by BGP community |
| Tops | Top Prefixes | Most-updated prefixes |
| Tops | Top L3VPN Prefixes | L3VPN equivalent |
| Link State | LS Nodes | IS-IS link-state node database |
| Link State | LS Links | IS-IS link-state link database |
| Link State | LS Topology | Network topology map |
| Link State | LS Prefixes | Link-state prefix database |
| Link State | LS History | Link-state change history |
| L3VPN | L3VPN Looking Glass | VPN RIB lookup |
| L3VPN | L3VPN Prefix History | VPN route change history |
| L3VPN | L3VPN RIB Browser | Full VPN RIB browser |
> History dashboards require `ip_rib_log` and `stats_chg_*` table data. Run `inject.py churn` to populate these.
---
## 9. Sanity Checks
### 9.1 All containers running
```bash
docker compose -p obmp ps
```
All containers should show `running`. If any are restarting, check logs:
```bash
docker compose -p obmp logs --tail=50 <service>
```
### 9.2 ExaBGP peers up
```bash
python3 exabgp/inject.py status
```
Both `10.100.0.100` and `10.100.0.200` should show `"state": "up"`.
Or check from the router side:
```
show bgp neighbors 10.40.40.202
show bgp summary | inc 10.40.40.202
```
### 9.3 Routes accepted by CORE routers
After loading `internet_sample`:
```bash
# On CORE-01 or CORE-02:
show bgp summary
# Expect: 77 accepted prefixes, 77 are bestpaths from 10.40.40.202
show bgp 8.8.8.0/24
# Expect: best path via 10.40.40.202 (eBGP), also iBGP copies from other routers
```
### 9.4 Routes in OpenBMP database
```bash
docker exec -it obmp-psql psql -U openbmp -c "
SELECT count(DISTINCT prefix) AS unique_prefixes,
count(DISTINCT peer_hash_id) AS peers_reporting
FROM ip_rib
WHERE isIPv4 = true AND isWithdrawn = false;
"
```
Expect `~129 unique prefixes` and `56 peers_reporting` (9 routers × ~6 peers each) after loading `internet_sample`.
### 9.5 Kafka is healthy
```bash
docker exec -it obmp-kafka kafka-topics --bootstrap-server localhost:29092 --list
```
Should show topics like `openbmp.parsed.unicast_prefix`, `openbmp.parsed.peer`, etc.
### 9.6 Grafana datasource
Open `http://10.40.40.202:3000` → Configuration → Data Sources → OpenBMP → Test.
Should return "Database Connection OK".
### 9.7 BMP collector receiving data
```bash
docker compose -p obmp logs --tail=30 collector
```
Should show connections from router management IPs.
### 9.8 psql-app consumer is caught up
```bash
docker compose -p obmp logs --tail=30 psql-app
```
Should show periodic cron job outputs (RPKI sync, IRR sync, global_ip_rib updates).
---
## 10. Relevant Commands Reference
### Docker Compose
```bash
# Start stack
OBMP_DATA_ROOT=/var/openbmp docker compose -p obmp up -d
# Stop stack
docker compose -p obmp down
# Show status
docker compose -p obmp ps
# Follow logs (all services)
docker compose -p obmp logs -f
# Follow logs (specific service)
docker compose -p obmp logs -f exabgp
docker compose -p obmp logs -f psql-app
docker compose -p obmp logs -f collector
# Rebuild and restart ExaBGP
docker compose -p obmp build exabgp && docker compose -p obmp up -d exabgp
# Restart a service
docker compose -p obmp restart psql-app
```
### Route Injection (from `exabgp/` directory)
```bash
# API health and peer states
python3 inject.py status
# List active routes
python3 inject.py routes
# List scenarios
python3 inject.py scenarios
# Load a scenario
python3 inject.py scenario internet_sample
python3 inject.py scenario churn
python3 inject.py scenario blackhole
python3 inject.py scenario full_table
python3 inject.py scenario lab_prefixes
# Withdraw a scenario
python3 inject.py withdraw-scenario internet_sample
# Withdraw all active routes
python3 inject.py withdraw-all
# Announce a specific prefix
python3 inject.py announce 10.0.0.0/8 --as-path 65100 3356 15169 --community 65100:100
# Withdraw a specific prefix
python3 inject.py withdraw 10.0.0.0/8
# Run churn (populate history tables)
python3 inject.py churn --count 5 --interval 30
```
### Database Queries
```bash
# Connect to database
docker exec -it obmp-psql psql -U openbmp -d openbmp
# Count unique prefixes in RIB
SELECT count(DISTINCT prefix) FROM ip_rib WHERE isIPv4=true AND isWithdrawn=false;
# Show recent route changes
SELECT prefix, origin_as, iswithdrawn, timestamp
FROM ip_rib_log
ORDER BY timestamp DESC LIMIT 20;
# Show peer summary
SELECT name, state, timestamp_last_updated
FROM bgp_peers
ORDER BY state, name;
# Show routes from ExaBGP peer
SELECT prefix, origin_as, as_path
FROM ip_rib
WHERE peer_hash_id IN (
SELECT hash_id FROM bgp_peers WHERE peer_addr = '10.40.40.202'
)
AND isWithdrawn = false;
```
### IOS-XR Verification (on router CLI)
```
show bgp neighbors 10.40.40.202
show bgp neighbors 10.40.40.202 received routes
show bgp summary
show bgp 8.8.8.0/24
show bgp 1.1.1.0/24
show route 8.8.8.0/24
```
---
## 11. Troubleshooting
### ExaBGP container keeps restarting
Check logs:
```bash
docker compose -p obmp logs --tail=50 exabgp
```
Common causes and fixes:
| Symptom | Cause | Fix |
|---------|-------|-----|
| Exits after "welcome" banner | Missing or wrong env file path | `startup.sh` generates `/usr/local/etc/exabgp/exabgp.env` — verify this path exists in container |
| Process `api` killed 5 times | Wrong Python path in conf | Conf uses `/usr/local/bin/python3` — correct for python:3.11-slim |
| `drop = true` in env | ExaBGP drops privileges to nobody, can't bind 179 | `startup.sh` patches `drop = false` — check the sed lines ran |
| `__pycache__ Permission denied` during build | Root-owned cache from previous container run | `.dockerignore` excludes `**/__pycache__` — confirm file exists |
### BGP sessions not establishing
1. Verify host IP `10.40.40.202` is reachable from CML management network: `ping 10.40.40.202` from router
2. Check ExaBGP peer state: `python3 exabgp/inject.py status`
3. On router: `show bgp neighbors 10.40.40.202` — look for error codes
4. Common IOS-XR errors:
- `no-update-source-config` — add `update-source MgmtEth0/RP0/CPU0/0`
- `no-ipv6-address` — ensure only IPv4 unicast AF is configured (no IPv6)
- TCP refused — check port 179 is reachable (ExaBGP uses `network_mode: host`)
### Routes received but not bestpath
IOS-XR BGP requires a specific route to resolve the BGP next-hop (10.40.40.202). The default route (0.0.0.0/0) is insufficient.
```
router static
address-family ipv4 unicast
10.40.40.0/24 10.100.0.254
```
Verify: `show bgp 1.1.1.0/24` — should show `Status: s (active), bestpath`.
### Grafana shows no data
1. Check datasource: Configuration → Data Sources → OpenBMP → Test
2. Verify psql-app is writing: `docker compose -p obmp logs psql-app`
3. Check the database directly (see database queries above)
4. History dashboards need route churn — run `python3 inject.py churn`
### Kafka not starting
Zookeeper must be healthy first. Check:
```bash
docker compose -p obmp logs zookeeper
docker compose -p obmp restart kafka
```
### psql-app fails to start
Usually a PostgreSQL connection issue or schema mismatch. Check:
```bash
docker compose -p obmp logs psql-app
# If "relation does not exist" errors: re-trigger DB init
touch /var/openbmp/config/init_db
docker compose -p obmp restart psql-app
```
---
## 12. Data Retention
Configured in `docker-compose.yml` via `POSTGRES_DROP_*` environment variables:
| Table | Default Retention |
|-------|-------------------|
| peer_event_log | 1 year |
| stat_reports | 4 weeks |
| ip_rib_log | 4 weeks |
| alerts | 4 weeks |
| ls_nodes_log | 4 months |
| ls_links_log | 4 months |
| ls_prefixes_log | 4 months |
| stats_chg_byprefix | 4 weeks |
| stats_chg_byasn | 4 weeks |
| stats_chg_bypeer | 4 weeks |
| stats_ip_origins | 4 weeks |
| stats_peer_rib | 4 weeks |
| stats_peer_update_counts | 4 weeks |
Adjust in `docker-compose.yml` under the `psql-app` service environment block.
---
## 13. Environment Variables Reference
### ExaBGP container
| Variable | Default | Description |
|----------|---------|-------------|
| `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_PEER_AS` | `65020` | AS of the IOS-XR lab |
| `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 |
### psql-app container (key variables)
| Variable | Default | Description |
|----------|---------|-------------|
| `MEM` | `3` | JVM heap in GB |
| `ENABLE_RPKI` | `1` | Enable RPKI sync from Cloudflare |
| `ENABLE_IRR` | `1` | Enable IRR sync |
| `ENABLE_DBIP` | `1` | Enable DB-IP geolocation import |
| `POSTGRES_REPORT_WINDOW` | `8 minute` | Aggregation window for summary tables |
### inject.py (CLI)
| Variable | Default | Description |
|----------|---------|-------------|
| `EXABGP_API` | `http://localhost:5050` | ExaBGP API base URL |

View File

@ -18,9 +18,9 @@
# #
# Example docker build: # Example docker build:
# tar -c -C ../../ ./obmp-collector ./obmp-docker/collector \ # tar -c -C ../../ ./obmp-collector ./obmp-docker/collector \
# | docker buildx build --progress=plain \ # | docker buildx build --platform linux/amd64 --progress=plain \
# --build-arg VERSION=2.1.1 \ # --build-arg VERSION=2.2.3 \
# -f obmp-docker/collector/Dockerfile -t openbmp/collector:2.1.1 - # -f obmp-docker/collector/Dockerfile -t openbmp/collector:2.2.3 -
# #
# ----------------------------------------------- # -----------------------------------------------
@ -32,7 +32,7 @@ COPY obmp-collector/ /ws
WORKDIR /ws WORKDIR /ws
RUN rm -rf build && mkdir -p build && cd build \ RUN rm -rf build && mkdir -p build && cd build \
&& cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr ../ \ && cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX:PATH=/usr ../ \
&& make \ && make \
&& make install && make install
@ -41,7 +41,7 @@ RUN rm -rf build && mkdir -p build && cd build \
# stage: Final container # stage: Final container
# ----------------------------------------------- # -----------------------------------------------
# Pull base image. # Pull base image.
FROM debian:bullseye-slim FROM debian:stable-slim
# Add files. # Add files.
ADD --chmod=755 obmp-docker/collector/scripts/install /tmp/ ADD --chmod=755 obmp-docker/collector/scripts/install /tmp/
@ -52,9 +52,6 @@ ARG VERSION=0
# Copy files from previous stages # Copy files from previous stages
COPY --chmod=755 --from=build /usr/bin/openbmpd /usr/bin/ COPY --chmod=755 --from=build /usr/bin/openbmpd /usr/bin/
COPY --from=build /usr/etc/openbmp/openbmpd.conf /usr/etc/openbmp/openbmpd.conf COPY --from=build /usr/etc/openbmp/openbmpd.conf /usr/etc/openbmp/openbmpd.conf
COPY --from=build /etc/init/openbmpd.conf /etc/init/openbmpd.conf
COPY --from=build /etc/default/openbmpd.new /etc/default/openbmpd
COPY --from=build /etc/logrotate.d/openbmpd /etc/logrotate.d/openbmpd
# Proxy servers # Proxy servers
#ENV http_proxy http://proxy:80 #ENV http_proxy http://proxy:80

View File

@ -6,9 +6,9 @@
# #
# Author: Tim Evens <tim@openbmp.org> # Author: Tim Evens <tim@openbmp.org>
# #
# BUILD: docker buildx build --progress=plain -t openbmp/dev-image:latest . # BUILD: docker buildx build --platform linux/amd64 --progress=plain -t openbmp/dev-image:latest .
FROM debian:bullseye-slim AS build FROM debian:stable-slim AS build
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
@ -18,8 +18,27 @@ WORKDIR /ws
RUN apt-get update RUN apt-get update
RUN apt-get install -y openjdk-17-jdk-headless maven RUN apt-get install -y openjdk-17-jdk-headless maven
RUN mkdir -p /usr/share/man/man1/ \ RUN mkdir -p /usr/share/man/man1/ \
&& apt-get -y install git gcc g++ libboost-dev cmake zlib1g-dev libssl-dev libsasl2-dev \ && apt-get -y install git gcc g++ libboost-dev cmake libssl-dev libsasl2-dev \
liblz4-dev libzstd-dev librdkafka-dev curl wget libgss-dev liblz4-dev libzstd-dev
# zlib1g-dev
# Build/install zlib - zlib1g-dev does not work for static builds of librdkafka
RUN cd /tmp && git clone https://github.com/madler/zlib.git \
&& cd zlib \
&& git checkout v1.2.12 \
&& CFLAGS=-fPIC ./configure --static \
&& make install
# Build/install librdkafka
RUN cd /tmp && git clone https://github.com/edenhill/librdkafka.git \
&& cd librdkafka \
&& git checkout v1.9.2 \
&& ./configure --enable-static --disable-curl \
&& make \
&& make install
# Installed under /usr/local/lib
# Build/install yaml-cpp # Build/install yaml-cpp
RUN cd /tmp && git clone https://github.com/jbeder/yaml-cpp.git \ RUN cd /tmp && git clone https://github.com/jbeder/yaml-cpp.git \
&& cd yaml-cpp \ && cd yaml-cpp \

View File

@ -1,11 +1,23 @@
--- ---
version: '3' version: '3'
volumes:
data-volume:
driver_opts:
type: none
device: ${OBMP_DATA_ROOT}/postgres/data
o: bind
ts-volume:
driver_opts:
type: none
device: ${OBMP_DATA_ROOT}/postgres/ts
o: bind
services: services:
zookeeper: zookeeper:
restart: unless-stopped restart: unless-stopped
container_name: obmp-zookeeper container_name: obmp-zookeeper
image: confluentinc/cp-zookeeper:7.0.1 image: confluentinc/cp-zookeeper:7.1.1
volumes: volumes:
- ${OBMP_DATA_ROOT}/zk-data:/var/lib/zookeeper/data - ${OBMP_DATA_ROOT}/zk-data:/var/lib/zookeeper/data
- ${OBMP_DATA_ROOT}/zk-log:/var/lib/zookeeper/log - ${OBMP_DATA_ROOT}/zk-log:/var/lib/zookeeper/log
@ -16,7 +28,7 @@ services:
kafka: kafka:
restart: unless-stopped restart: unless-stopped
container_name: obmp-kafka container_name: obmp-kafka
image: confluentinc/cp-kafka:7.0.1 image: confluentinc/cp-kafka:7.1.1
# Change the mount point to where you want to store Kafka data. # Change the mount point to where you want to store Kafka data.
# Normally 80GB or more # Normally 80GB or more
@ -33,13 +45,12 @@ services:
# Change/add listeners based on your FQDN that the host and other containers can access. You can use # Change/add listeners based on your FQDN that the host and other containers can access. You can use
# an IP address as well. By default, only within the compose/containers can Kafka be accesssed # an IP address as well. By default, only within the compose/containers can Kafka be accesssed
# using port 29092. Outside access can be enabled, but you should use an FQDN listener. # using port 29092. Outside access can be enabled, but you should use an FQDN listener.
#KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://<FQDN>:9092 KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://obmp-kafka:29092,PLAINTEXT_HOST://10.40.40.202:9092
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://obmp-kafka:29092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_NUM_PARTITIONS: 8 KAFKA_NUM_PARTITIONS: 8
KAFKA_LOG_RETENTION_MINUTES: 90 KAFKA_LOG_RETENTION_MINUTES: 720
KAFKA_LOG_ROLL_MS: 3600000 KAFKA_LOG_ROLL_MS: 3600000
KAFKA_LOG_SEGMENT_BYTES: 1073741824 KAFKA_LOG_SEGMENT_BYTES: 1073741824
KAFKA_MESSAGE_MAX_BYTES: 100000000 KAFKA_MESSAGE_MAX_BYTES: 100000000
@ -73,7 +84,7 @@ services:
grafana: grafana:
restart: unless-stopped restart: unless-stopped
container_name: obmp-grafana container_name: obmp-grafana
image: grafana/grafana:8.5.2 image: grafana/grafana:9.1.7
ports: ports:
- "3000:3000" - "3000:3000"
volumes: volumes:
@ -107,7 +118,8 @@ services:
psql: psql:
restart: unless-stopped restart: unless-stopped
container_name: obmp-psql container_name: obmp-psql
image: openbmp/postgres:2.1.1 image: openbmp/postgres:2.2.1
privileged: true
shm_size: 1536m shm_size: 1536m
sysctls: sysctls:
- net.ipv4.tcp_keepalive_intvl=30 - net.ipv4.tcp_keepalive_intvl=30
@ -116,8 +128,8 @@ services:
ports: ports:
- "5432:5432" - "5432:5432"
volumes: volumes:
- ${OBMP_DATA_ROOT}/postgres/data:/var/lib/postgresql/data # change this to 80GB SSD slice/partition - data-volume:/var/lib/postgresql/data
- ${OBMP_DATA_ROOT}/postgres/ts:/var/lib/postgresql/ts # Chnage this to 500GB SSD slice/partition - ts-volume:/var/lib/postgresql/ts
# alter_job max_runtime in _timescaledb_config.bgw_job ( https://docs.timescale.com/latest/api#alter_job ) # alter_job max_runtime in _timescaledb_config.bgw_job ( https://docs.timescale.com/latest/api#alter_job )
command: > command: >
-c max_wal_size=10GB -c max_wal_size=10GB
@ -129,7 +141,7 @@ services:
collector: collector:
restart: unless-stopped restart: unless-stopped
container_name: obmp-collector container_name: obmp-collector
image: openbmp/collector:2.1.1 image: openbmp/collector:2.2.3
sysctls: sysctls:
- net.ipv4.tcp_keepalive_intvl=30 - net.ipv4.tcp_keepalive_intvl=30
- net.ipv4.tcp_keepalive_probes=5 - net.ipv4.tcp_keepalive_probes=5
@ -144,7 +156,7 @@ services:
psql-app: psql-app:
restart: unless-stopped restart: unless-stopped
container_name: obmp-psql-app container_name: obmp-psql-app
image: openbmp/psql-app:2.1.1 image: openbmp/psql-app:2.2.2
sysctls: sysctls:
- net.ipv4.tcp_keepalive_intvl=30 - net.ipv4.tcp_keepalive_intvl=30
- net.ipv4.tcp_keepalive_probes=5 - net.ipv4.tcp_keepalive_probes=5
@ -162,6 +174,7 @@ services:
- RPKI_USER=None - RPKI_USER=None
- ENABLE_RPKI=1 # 1 enables, 0 disables RPKI sync - ENABLE_RPKI=1 # 1 enables, 0 disables RPKI sync
- ENABLE_IRR=1 # 1 enables, 0 disables IRR sync - ENABLE_IRR=1 # 1 enables, 0 disables IRR sync
- ENABLE_DBIP=1 # 1 enables, 0 disables DBIP import
- POSTGRES_REPORT_WINDOW='8 minute' # default POSTGRESS window to select when building - POSTGRES_REPORT_WINDOW='8 minute' # default POSTGRESS window to select when building
# summary tables. For deployments that absorb large # summary tables. For deployments that absorb large
# bursts increase the value, ex 60 minute # bursts increase the value, ex 60 minute
@ -184,10 +197,34 @@ services:
- POSTGRES_DROP_stats_peer_rib='4 weeks' - POSTGRES_DROP_stats_peer_rib='4 weeks'
- POSTGRES_DROP_stats_peer_update_counts='4 weeks' - POSTGRES_DROP_stats_peer_update_counts='4 weeks'
exabgp:
restart: unless-stopped
container_name: obmp-exabgp
build:
context: ./exabgp
dockerfile: Dockerfile
# Host networking so ExaBGP can reach CML routers directly on port 179
network_mode: host
environment:
# IP on the host that CML routers can reach (matches Kafka external listener)
- EXABGP_LOCAL_IP=10.40.40.202
# ExaBGP presents as AS 65100 (eBGP peer to your AS 65020 lab)
- EXABGP_LOCAL_AS=65100
- EXABGP_PEER_AS=65020
# CORE routers to peer with — these propagate routes into the iBGP mesh
- EXABGP_PEER_1=10.100.0.100
- EXABGP_PEER_2=10.100.0.200
# Flask API port (also on host network)
- EXABGP_API_PORT=5050
volumes:
# Mount scenarios dir so you can edit/add scenarios without rebuilding
- ./exabgp/scenarios:/exabgp/scenarios
# No ports: block needed — network_mode: host exposes directly
whois: whois:
restart: unless-stopped restart: unless-stopped
container_name: obmp-whois container_name: obmp-whois
image: openbmp/whois:2.1.0 image: openbmp/whois:2.2.0
sysctls: sysctls:
- net.ipv4.tcp_keepalive_intvl=30 - net.ipv4.tcp_keepalive_intvl=30
- net.ipv4.tcp_keepalive_probes=5 - net.ipv4.tcp_keepalive_probes=5
@ -201,4 +238,4 @@ services:
- POSTGRES_USER=openbmp - POSTGRES_USER=openbmp
- POSTGRES_DB=openbmp - POSTGRES_DB=openbmp
- POSTGRES_HOST=obmp-psql - POSTGRES_HOST=obmp-psql
- POSTGRES_PORT=5432 - POSTGRES_PORT=5432

2
exabgp/.dockerignore Normal file
View File

@ -0,0 +1,2 @@
**/__pycache__
**/*.pyc

19
exabgp/Dockerfile Normal file
View File

@ -0,0 +1,19 @@
FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
procps \
&& rm -rf /var/lib/apt/lists/*
# Install ExaBGP and Flask API dependencies
RUN pip install --no-cache-dir exabgp flask requests
# ExaBGP writes its own config at startup; we just need the source tree
COPY . /exabgp/
WORKDIR /exabgp
RUN chmod +x /exabgp/startup.sh /exabgp/inject.py
# Flask API port
EXPOSE 5050
CMD ["/bin/bash", "/exabgp/startup.sh"]

View File

@ -0,0 +1,3 @@
exabgp
flask
requests

273
exabgp/api/server.py Normal file
View File

@ -0,0 +1,273 @@
#!/usr/bin/env python3
"""
ExaBGP Route Injection API Server
Runs as an ExaBGP process reads BGP events from stdin, writes BGP
commands to stdout. Flask API runs in a background thread so HTTP
requests trigger live route announcements/withdrawals.
API endpoints:
GET /healthz - health + active route count
GET /routes - list all active routes
POST /announce - announce prefixes
POST /withdraw - withdraw prefixes
POST /withdraw/all - withdraw everything
GET /scenarios - list available scenarios
POST /scenario/<name> - load a scenario
DELETE /scenario/<name> - withdraw a scenario
GET /peers - BGP peer state
"""
import sys
import os
import json
import threading
import time
import logging
from flask import Flask, request, jsonify
# --- logging to stderr so it doesn't pollute ExaBGP's stdout pipe ---
logging.basicConfig(
stream=sys.stderr,
level=logging.INFO,
format='%(asctime)s [API] %(levelname)s %(message)s',
)
log = logging.getLogger(__name__)
app = Flask(__name__)
# Thread-safe stdout for ExaBGP commands
_stdout_lock = threading.Lock()
# Active routes: prefix -> metadata dict
active_routes = {}
# Peer state received from ExaBGP events
peer_states = {}
# ---------------------------------------------------------------------------
# ExaBGP command helpers
# ---------------------------------------------------------------------------
def _send(cmd: str):
"""Write a command to ExaBGP via stdout."""
with _stdout_lock:
sys.stdout.write(cmd + '\n')
sys.stdout.flush()
log.info('→ ExaBGP: %s', cmd)
def _build_announce(prefix, next_hop='self', as_path=None, communities=None, med=None, local_pref=None):
parts = [f'announce route {prefix} next-hop {next_hop}']
if as_path:
parts.append(f'as-path [ {" ".join(str(a) for a in as_path)} ]')
if communities:
parts.append(f'community [ {" ".join(communities)} ]')
if med is not None:
parts.append(f'med {med}')
if local_pref is not None:
parts.append(f'local-preference {local_pref}')
return ' '.join(parts)
def _build_withdraw(prefix, next_hop='self'):
return f'withdraw route {prefix} next-hop {next_hop}'
def announce_route(prefix, next_hop='self', as_path=None, communities=None, med=None, local_pref=None):
cmd = _build_announce(prefix, next_hop, as_path, communities, med, local_pref)
_send(cmd)
active_routes[prefix] = {
'prefix': prefix,
'next_hop': next_hop,
'as_path': as_path or [],
'communities': communities or [],
'med': med,
'local_pref': local_pref,
'announced_at': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()),
}
def withdraw_route(prefix, next_hop='self'):
_send(_build_withdraw(prefix, next_hop))
active_routes.pop(prefix, None)
# ---------------------------------------------------------------------------
# Flask API routes
# ---------------------------------------------------------------------------
@app.route('/healthz')
def health():
return jsonify({
'status': 'ok',
'active_routes': len(active_routes),
'peers': peer_states,
})
@app.route('/routes', methods=['GET'])
def list_routes():
return jsonify({
'count': len(active_routes),
'routes': list(active_routes.values()),
})
@app.route('/announce', methods=['POST'])
def api_announce():
data = request.get_json(force=True)
prefixes = data.get('prefixes', [])
if not prefixes:
return jsonify({'error': 'prefixes list required'}), 400
next_hop = data.get('next_hop', 'self')
as_path = data.get('as_path', [])
communities = data.get('communities', [])
med = data.get('med')
local_pref = data.get('local_pref')
announced = []
for prefix in prefixes:
announce_route(prefix, next_hop, as_path, communities, med, local_pref)
announced.append(prefix)
return jsonify({'announced': announced, 'count': len(announced)})
@app.route('/withdraw', methods=['POST'])
def api_withdraw():
data = request.get_json(force=True)
prefixes = data.get('prefixes', [])
if not prefixes:
return jsonify({'error': 'prefixes list required'}), 400
withdrawn = []
for prefix in prefixes:
withdraw_route(prefix)
withdrawn.append(prefix)
return jsonify({'withdrawn': withdrawn, 'count': len(withdrawn)})
@app.route('/withdraw/all', methods=['POST'])
def api_withdraw_all():
prefixes = list(active_routes.keys())
for prefix in prefixes:
withdraw_route(prefix)
return jsonify({'withdrawn': prefixes, 'count': len(prefixes)})
# ---------------------------------------------------------------------------
# Scenario management
# ---------------------------------------------------------------------------
sys.path.insert(0, '/exabgp')
from scenarios import SCENARIOS
@app.route('/scenarios', methods=['GET'])
def list_scenarios():
return jsonify({
'scenarios': {
name: {
'description': s.get('description', ''),
'route_count': len(s.get('routes', [])),
}
for name, s in SCENARIOS.items()
}
})
@app.route('/scenario/<name>', methods=['POST'])
def load_scenario(name):
if name not in SCENARIOS:
return jsonify({'error': f'Unknown scenario: {name}. Available: {list(SCENARIOS)}'}), 404
scenario = SCENARIOS[name]
announced = []
for route in scenario['routes']:
prefix = route['prefix']
announce_route(
prefix,
next_hop=route.get('next_hop', 'self'),
as_path=route.get('as_path', []),
communities=route.get('communities', []),
med=route.get('med'),
local_pref=route.get('local_pref'),
)
announced.append(prefix)
log.info('Loaded scenario %s: %d routes', name, len(announced))
return jsonify({'scenario': name, 'announced': announced, 'count': len(announced)})
@app.route('/scenario/<name>', methods=['DELETE'])
def unload_scenario(name):
if name not in SCENARIOS:
return jsonify({'error': f'Unknown scenario: {name}'}), 404
scenario = SCENARIOS[name]
withdrawn = []
for route in scenario['routes']:
prefix = route['prefix']
if prefix in active_routes:
withdraw_route(prefix)
withdrawn.append(prefix)
log.info('Unloaded scenario %s: %d routes', name, len(withdrawn))
return jsonify({'scenario': name, 'withdrawn': withdrawn, 'count': len(withdrawn)})
@app.route('/peers', methods=['GET'])
def get_peers():
return jsonify({'peers': peer_states})
# ---------------------------------------------------------------------------
# ExaBGP event loop (main thread)
# ---------------------------------------------------------------------------
def parse_exabgp_event(line: str):
"""Parse ExaBGP neighbor state events from stdin."""
# ExaBGP text events look like:
# neighbor 10.100.0.100 up
# neighbor 10.100.0.100 down
parts = line.strip().split()
if len(parts) >= 3 and parts[0] == 'neighbor':
peer_ip = parts[1]
state = parts[2]
peer_states[peer_ip] = {'state': state, 'updated': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())}
log.info('Peer %s%s', peer_ip, state)
def main():
api_port = int(os.environ.get('EXABGP_API_PORT', 5050))
# Start Flask in background thread
flask_thread = threading.Thread(
target=lambda: app.run(host='0.0.0.0', port=api_port, threaded=True),
daemon=True,
)
flask_thread.start()
log.info('Flask API listening on port %d', api_port)
# Main thread: read ExaBGP events from stdin
log.info('Waiting for ExaBGP events on stdin...')
while True:
try:
line = sys.stdin.readline()
if not line:
log.warning('ExaBGP stdin closed, exiting')
break
line = line.strip()
if line:
log.debug('← ExaBGP: %s', line)
parse_exabgp_event(line)
except KeyboardInterrupt:
break
except Exception as e:
log.error('Event loop error: %s', e)
if __name__ == '__main__':
main()

213
exabgp/inject.py Normal file
View File

@ -0,0 +1,213 @@
#!/usr/bin/env python3
"""
inject.py CLI wrapper for the ExaBGP Route Injection API
Usage:
inject.py status
inject.py routes
inject.py scenarios
inject.py announce <prefix> [<prefix>...] [--as-path ASN...] [--community STR...] [--med N] [--next-hop IP]
inject.py withdraw <prefix> [<prefix>...]
inject.py withdraw-all
inject.py scenario <name>
inject.py withdraw-scenario <name>
inject.py churn [--count N] [--interval SEC] # cycle announce/withdraw for ip_rib_log population
Environment:
EXABGP_API=http://localhost:5050 API base URL
"""
import sys
import os
import json
import time
import argparse
import requests
API = os.environ.get('EXABGP_API', 'http://localhost:5050')
def _post(path, data=None):
r = requests.post(f'{API}{path}', json=data or {}, timeout=10)
r.raise_for_status()
return r.json()
def _delete(path):
r = requests.delete(f'{API}{path}', timeout=10)
r.raise_for_status()
return r.json()
def _get(path):
r = requests.get(f'{API}{path}', timeout=10)
r.raise_for_status()
return r.json()
def _pp(data):
print(json.dumps(data, indent=2))
def cmd_status(args):
_pp(_get('/healthz'))
def cmd_routes(args):
data = _get('/routes')
print(f"Active routes: {data['count']}")
for r in data['routes']:
path = ' '.join(str(a) for a in r.get('as_path', []))
comms = ' '.join(r.get('communities', []))
parts = [r['prefix'], f'nh={r["next_hop"]}']
if path:
parts.append(f'as-path=[{path}]')
if comms:
parts.append(f'community=[{comms}]')
if r.get('med') is not None:
parts.append(f'med={r["med"]}')
print(' ' + ' '.join(parts))
def cmd_scenarios(args):
data = _get('/scenarios')
print(f"{'Name':<20} {'Routes':>6} Description")
print('-' * 70)
for name, info in data['scenarios'].items():
print(f"{name:<20} {info['route_count']:>6} {info['description']}")
def cmd_announce(args):
payload = {
'prefixes': args.prefixes,
'next_hop': args.next_hop,
'as_path': [int(a) for a in args.as_path] if args.as_path else [],
'communities': args.community or [],
}
if args.med is not None:
payload['med'] = args.med
data = _post('/announce', payload)
print(f"Announced {data['count']} route(s):")
for p in data['announced']:
print(f" + {p}")
def cmd_withdraw(args):
data = _post('/withdraw', {'prefixes': args.prefixes})
print(f"Withdrew {data['count']} route(s):")
for p in data['withdrawn']:
print(f" - {p}")
def cmd_withdraw_all(args):
data = _post('/withdraw/all')
print(f"Withdrew all {data['count']} route(s)")
def cmd_scenario(args):
data = _post(f'/scenario/{args.name}')
print(f"Loaded scenario '{args.name}': {data['count']} routes announced")
def cmd_withdraw_scenario(args):
data = _delete(f'/scenario/{args.name}')
print(f"Withdrew scenario '{args.name}': {data['count']} routes withdrawn")
def cmd_churn(args):
"""
Cycle announce/withdraw on the 'churn' scenario to generate ip_rib_log
entries and populate stats_chg_* tables in OpenBMP.
"""
count = args.count
interval = args.interval
print(f"Starting churn: {count} cycles, {interval}s interval")
print("This will populate ip_rib_log and stats_chg_* tables in OpenBMP.")
print("Press Ctrl+C to stop.\n")
cycle = 0
try:
while count == 0 or cycle < count:
cycle += 1
print(f"Cycle {cycle}: announcing churn scenario...")
r = _post('/scenario/churn')
print(f" + {r['count']} routes announced")
time.sleep(interval)
print(f"Cycle {cycle}: withdrawing churn scenario...")
r = _delete('/scenario/churn')
print(f" - {r['count']} routes withdrawn")
time.sleep(interval)
print()
except KeyboardInterrupt:
print("\nChurn stopped. Withdrawing any active routes...")
_post('/withdraw/all')
print("Done.")
def main():
parser = argparse.ArgumentParser(
description='ExaBGP Route Injection CLI',
formatter_class=argparse.RawDescriptionHelpFormatter,
)
sub = parser.add_subparsers(dest='command')
sub.add_parser('status', help='Show API health and peer states')
sub.add_parser('routes', help='List active announced routes')
sub.add_parser('scenarios', help='List available scenarios')
sub.add_parser('withdraw-all', help='Withdraw all active routes')
p = sub.add_parser('announce', help='Announce one or more prefixes')
p.add_argument('prefixes', nargs='+')
p.add_argument('--as-path', nargs='+', default=[], metavar='ASN')
p.add_argument('--community', nargs='+', default=[], metavar='COMM')
p.add_argument('--med', type=int, default=None)
p.add_argument('--next-hop', default='self', metavar='IP')
p = sub.add_parser('withdraw', help='Withdraw one or more prefixes')
p.add_argument('prefixes', nargs='+')
p = sub.add_parser('scenario', help='Load a named scenario')
p.add_argument('name')
p = sub.add_parser('withdraw-scenario', help='Withdraw a named scenario')
p.add_argument('name')
p = sub.add_parser('churn', help='Cycle announce/withdraw to populate ip_rib_log')
p.add_argument('--count', type=int, default=0, metavar='N',
help='Number of cycles (0 = infinite)')
p.add_argument('--interval', type=float, default=30.0, metavar='SEC',
help='Seconds between announce and withdraw (default: 30)')
args = parser.parse_args()
cmds = {
'status': cmd_status,
'routes': cmd_routes,
'scenarios': cmd_scenarios,
'announce': cmd_announce,
'withdraw': cmd_withdraw,
'withdraw-all': cmd_withdraw_all,
'scenario': cmd_scenario,
'withdraw-scenario': cmd_withdraw_scenario,
'churn': cmd_churn,
}
if not args.command:
parser.print_help()
sys.exit(1)
try:
cmds[args.command](args)
except requests.exceptions.ConnectionError:
print(f"ERROR: Cannot connect to ExaBGP API at {API}")
print("Is the exabgp container running? docker compose logs exabgp")
sys.exit(1)
except requests.exceptions.HTTPError as e:
print(f"ERROR: {e}")
sys.exit(1)
if __name__ == '__main__':
main()

172
exabgp/iosxr_bgp_config.md Normal file
View File

@ -0,0 +1,172 @@
# IOS-XR BGP Config for ExaBGP Peering
Apply to **both CORE routers** (`CML-R9K-CORE-01` @ 10.100.0.100 and
`CML-R9K-CORE-02` @ 10.100.0.200).
Credentials: `ssh user@<mgmt-ip>` password `cisco`
---
## Route Policy (apply once per router)
```
route-policy EXABGP_IN
pass
end-policy
route-policy EXABGP_OUT
drop
end-policy
```
---
## BGP Neighbor Block
```
router bgp 65020
neighbor 10.40.40.202
remote-as 65100
description ExaBGP-Route-Injector
ebgp-multihop 5
update-source MgmtEth0/RP0/CPU0/0
!
address-family ipv4 unicast
route-policy EXABGP_IN in
route-policy EXABGP_OUT out
next-hop-self
!
address-family ipv6 unicast
route-policy EXABGP_IN in
route-policy EXABGP_OUT out
next-hop-self
!
!
!
```
### Key config notes
| Knob | Why |
|------|-----|
| `remote-as 65100` | ExaBGP presents as AS 65100 (eBGP to your AS 65020 mesh) |
| `ebgp-multihop 5` | Host (10.40.40.202) and router (10.100.0.x) are different subnets |
| `update-source MgmtEth0/RP0/CPU0/0` | Use management interface for the TCP session |
| `next-hop-self` | Replace ExaBGP's next-hop (10.40.40.202) with the CORE router's loopback when reflecting into iBGP — ensures other routers can resolve the next-hop via ISIS |
| `EXABGP_OUT` drops | Prevent the lab from advertising its own prefixes back to ExaBGP |
---
## Verification Commands
```
show bgp neighbors 10.40.40.202
show bgp neighbors 10.40.40.202 received routes
show bgp summary
show route 1.1.1.0/24
show bgp 1.1.1.0/24
```
After loading the `internet_sample` scenario you should see ~94 new
prefixes in the BGP table and they should propagate to all 9 routers
via the iBGP mesh.
---
## NETCONF push (alternative to CLI)
With NETCONF already enabled, you can push this config programmatically:
```bash
pip install ncclient
python3 - <<'EOF'
from ncclient import manager
ROUTERS = [
{'host': '10.100.0.100', 'description': 'CML-R9K-CORE-01'},
{'host': '10.100.0.200', 'description': 'CML-R9K-CORE-02'},
]
POLICY_XML = """
<config>
<routing-policy xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-policy-repository-cfg">
<route-policies>
<route-policy>
<route-policy-name>EXABGP_IN</route-policy-name>
<rpl-route-policy>route-policy EXABGP_IN&#10; pass&#10;end-policy</rpl-route-policy>
</route-policy>
<route-policy>
<route-policy-name>EXABGP_OUT</route-policy-name>
<rpl-route-policy>route-policy EXABGP_OUT&#10; drop&#10;end-policy</rpl-route-policy>
</route-policy>
</route-policies>
</routing-policy>
<bgp xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ipv4-bgp-cfg">
<instance>
<instance-name>default</instance-name>
<instance-as>
<as>0</as>
<four-byte-as>
<as>65020</as>
<bgp-running/>
<default-vrf>
<bgp-entity>
<neighbors>
<neighbor>
<neighbor-address>10.40.40.202</neighbor-address>
<remote-as>
<as-xx>0</as-xx>
<as-yy>65100</as-yy>
</remote-as>
<description>ExaBGP-Route-Injector</description>
<ebgp-multihop>
<max-hop-count>5</max-hop-count>
</ebgp-multihop>
<update-in-filtering>
<enable/>
</update-in-filtering>
<neighbor-afs>
<neighbor-af>
<af-name>ipv4-unicast</af-name>
<activate/>
<route-policy-in>EXABGP_IN</route-policy-in>
<route-policy-out>EXABGP_OUT</route-policy-out>
<next-hop-self>true</next-hop-self>
</neighbor-af>
<neighbor-af>
<af-name>ipv6-unicast</af-name>
<activate/>
<route-policy-in>EXABGP_IN</route-policy-in>
<route-policy-out>EXABGP_OUT</route-policy-out>
<next-hop-self>true</next-hop-self>
</neighbor-af>
</neighbor-afs>
</neighbor>
</neighbors>
</bgp-entity>
</default-vrf>
</four-byte-as>
</instance-as>
</instance>
</bgp>
</config>
"""
for router in ROUTERS:
print(f"Configuring {router['description']} ({router['host']})...")
with manager.connect(
host=router['host'],
port=830,
username='user',
password='cisco',
hostkey_verify=False,
device_params={'name': 'iosxr'},
) as m:
m.edit_config(target='candidate', config=POLICY_XML)
m.commit()
print(f" Done.")
print("All routers configured.")
EOF
```

View File

@ -0,0 +1,315 @@
"""
BGP Route Injection Scenarios
Each scenario is a dict with:
description: str
routes: list of route dicts
Route dict keys:
prefix (required) e.g. "1.1.1.0/24"
next_hop default "self"
as_path list of ASNs, e.g. [65100, 1299, 15169]
communities list of strings, e.g. ["65100:100"]
med int, optional
local_pref int, optional
Well-known transit ASNs used in AS paths:
174 Cogent
701 Verizon / MCI
1299 Telia
2914 NTT
3257 GTT
3356 Lumen / Level3
6461 Zayo
6762 Sparkle / Telecom Italia
7018 AT&T
"""
# ---------------------------------------------------------------------------
# Helper to build route dicts quickly
# ---------------------------------------------------------------------------
def _r(prefix, as_path, communities=None, med=None, local_pref=None):
return {
'prefix': prefix,
'next_hop': 'self',
'as_path': as_path,
'communities': communities or [],
'med': med,
'local_pref': local_pref,
}
# ---------------------------------------------------------------------------
# Scenario: internet_sample
# Partial internet table — realistic mix of prefix lengths and AS paths.
# Prefixes are real public ranges with synthetic (but plausible) AS paths.
# ---------------------------------------------------------------------------
_INTERNET_V4 = [
# Cloudflare
_r('1.1.1.0/24', [65100, 174, 13335]),
_r('1.0.0.1/32', [65100, 3356, 13335]),
_r('104.16.0.0/13', [65100, 1299, 13335]),
_r('104.24.0.0/14', [65100, 6461, 13335]),
_r('162.158.0.0/15', [65100, 7018, 13335]),
_r('172.64.0.0/13', [65100, 2914, 13335]),
_r('198.41.128.0/17', [65100, 3257, 13335]),
# Google / Alphabet
_r('8.8.8.0/24', [65100, 3356, 15169]),
_r('8.8.4.0/24', [65100, 1299, 15169]),
_r('8.34.208.0/20', [65100, 6762, 15169]),
_r('34.0.0.0/15', [65100, 701, 15169]),
_r('35.190.0.0/17', [65100, 2914, 15169]),
_r('64.233.160.0/19', [65100, 3356, 15169]),
_r('66.102.0.0/20', [65100, 7018, 15169]),
_r('74.125.0.0/16', [65100, 174, 15169]),
_r('142.250.0.0/15', [65100, 3257, 15169]),
_r('172.217.0.0/16', [65100, 6461, 15169]),
_r('216.58.192.0/19', [65100, 1299, 15169]),
# Amazon AWS
_r('3.0.0.0/15', [65100, 3356, 16509]),
_r('13.32.0.0/15', [65100, 1299, 16509]),
_r('52.0.0.0/14', [65100, 6461, 16509]),
_r('52.84.0.0/15', [65100, 2914, 16509]),
_r('54.64.0.0/13', [65100, 701, 16509]),
_r('54.144.0.0/12', [65100, 174, 16509]),
_r('54.160.0.0/13', [65100, 3257, 16509]),
_r('99.77.128.0/18', [65100, 7018, 16509]),
_r('205.251.192.0/18',[65100, 3356, 16509]),
# Microsoft Azure
_r('13.64.0.0/11', [65100, 1299, 8075]),
_r('20.0.0.0/14', [65100, 6762, 8075]),
_r('20.33.0.0/16', [65100, 3356, 8075]),
_r('40.64.0.0/10', [65100, 2914, 8075]),
_r('52.224.0.0/11', [65100, 701, 8075]),
_r('104.40.0.0/13', [65100, 174, 8075]),
_r('168.61.0.0/16', [65100, 7018, 8075]),
# Akamai
_r('23.0.0.0/12', [65100, 3356, 20940]),
_r('23.32.0.0/11', [65100, 1299, 20940]),
_r('23.192.0.0/11', [65100, 6461, 20940]),
_r('92.122.0.0/15', [65100, 2914, 20940]),
_r('95.100.0.0/15', [65100, 3257, 20940]),
_r('184.24.0.0/13', [65100, 7018, 20940]),
# Fastly CDN
_r('23.235.32.0/20', [65100, 174, 54113]),
_r('103.244.50.0/24', [65100, 3356, 54113]),
_r('151.101.0.0/16', [65100, 1299, 54113]),
_r('157.52.192.0/18', [65100, 6461, 54113]),
_r('185.31.16.0/22', [65100, 2914, 54113]),
_r('199.27.72.0/21', [65100, 701, 54113]),
# Twitter / X
_r('104.244.42.0/24', [65100, 3356, 13414]),
_r('192.133.76.0/22', [65100, 1299, 13414]),
# Meta / Facebook
_r('31.13.24.0/21', [65100, 174, 32934]),
_r('31.13.64.0/18', [65100, 6762, 32934]),
_r('66.220.144.0/20', [65100, 7018, 32934]),
_r('69.63.176.0/20', [65100, 2914, 32934]),
_r('69.171.224.0/19', [65100, 3257, 32934]),
_r('157.240.0.0/17', [65100, 3356, 32934]),
_r('185.89.218.0/23', [65100, 701, 32934]),
_r('204.15.20.0/22', [65100, 1299, 32934]),
# Apple
_r('17.0.0.0/8', [65100, 1299, 714]),
_r('17.172.224.0/19', [65100, 6461, 714]),
_r('17.178.96.0/19', [65100, 2914, 714]),
_r('192.35.50.0/24', [65100, 3356, 714]),
# Comcast
_r('50.18.0.0/16', [65100, 7018, 7922]),
_r('73.0.0.0/8', [65100, 174, 7922]),
_r('96.0.0.0/11', [65100, 3257, 7922]),
# Verizon
_r('70.0.0.0/11', [65100, 3356, 701]),
_r('98.0.0.0/10', [65100, 1299, 701]),
_r('174.0.0.0/12', [65100, 6461, 701]),
# Generic transit destinations for AS path variety
_r('5.0.0.0/16', [65100, 1299, 6762, 34984]),
_r('45.86.0.0/16', [65100, 3257, 9002, 51847]),
_r('80.64.0.0/18', [65100, 174, 1239, 34224]),
_r('82.112.0.0/15', [65100, 6461, 5400, 12322]),
_r('89.0.0.0/17', [65100, 2914, 3491, 8551]),
_r('91.108.4.0/22', [65100, 701, 9002, 42831]),
_r('141.0.0.0/16', [65100, 7018, 1239, 6830]),
_r('185.0.0.0/22', [65100, 3356, 5400, 44946]),
_r('195.0.0.0/21', [65100, 1299, 3491, 30781]),
_r('212.0.0.0/16', [65100, 6762, 9002, 3301]),
_r('217.0.0.0/20', [65100, 174, 1239, 25160]),
]
_INTERNET_V6 = [
_r('2001:4860::/32', [65100, 3356, 15169]), # Google
_r('2001:4860:4860::/48', [65100, 1299, 15169]),
_r('2606:4700::/32', [65100, 174, 13335]), # Cloudflare
_r('2606:4700:4700::/48', [65100, 3356, 13335]),
_r('2400:cb00::/32', [65100, 2914, 13335]),
_r('2620:0:2d0::/48', [65100, 701, 2906]), # Netflix
_r('2600::/23', [65100, 6461, 16509]), # Amazon
_r('2a00:1450::/32', [65100, 1299, 15169]), # Google EU
_r('2001:8d8::/32', [65100, 3257, 20940]), # Akamai
_r('2620:1ec::/36', [65100, 7018, 8075]), # Microsoft
_r('2a03:2880::/32', [65100, 3356, 32934]), # Meta
_r('2001:df0::/32', [65100, 2914, 4837]), # China Unicom
_r('2001:500::/30', [65100, 174, 3356]), # ARIN
_r('2001:db8::/32', [65100, 1299, 65001]), # Documentation (RFC 3849)
]
# ---------------------------------------------------------------------------
# Scenario: churn
# 30 prefixes designed to be announced then withdrawn repeatedly.
# Load with /scenario/churn, withdraw with DELETE /scenario/churn.
# Run announce→withdraw→announce cycles to populate ip_rib_log.
# ---------------------------------------------------------------------------
_CHURN_PREFIXES = [
'198.51.100.0/24', # RFC 5737 documentation space
'198.51.101.0/24',
'198.51.102.0/24',
'198.51.103.0/24',
'198.51.104.0/24',
'198.51.105.0/24',
'198.51.106.0/24',
'198.51.107.0/24',
'198.51.108.0/24',
'198.51.109.0/24',
'203.0.113.0/24', # RFC 5737 documentation space
'203.0.113.1/32',
'203.0.113.2/32',
'203.0.113.3/32',
'203.0.113.4/32',
'100.64.0.0/24', # RFC 6598 shared address space
'100.64.1.0/24',
'100.64.2.0/24',
'100.64.3.0/24',
'100.64.4.0/24',
'192.0.2.0/24', # RFC 5737
'192.0.2.128/25',
'192.0.2.0/25',
'192.0.3.0/24',
'192.0.4.0/24',
'192.0.5.0/24',
'192.0.6.0/24',
'192.0.7.0/24',
'192.0.8.0/24',
'192.0.9.0/24',
]
_CHURN_ROUTES = [
_r(p, [65100, 65200], communities=['65100:200'])
for p in _CHURN_PREFIXES
]
# ---------------------------------------------------------------------------
# Scenario: blackhole
# Prefixes with RTBH (Remotely Triggered Black Hole) community.
# Community 65100:666 signals black-hole intent.
# Also includes the well-known BLACKHOLE community (65535:666).
# ---------------------------------------------------------------------------
_BLACKHOLE_ROUTES = [
_r('192.0.2.1/32', [65100], communities=['65100:666', '65535:666']),
_r('192.0.2.2/32', [65100], communities=['65100:666', '65535:666']),
_r('192.0.2.3/32', [65100], communities=['65100:666', '65535:666']),
_r('198.51.100.1/32',[65100], communities=['65100:666', '65535:666']),
_r('198.51.100.2/32',[65100], communities=['65100:666', '65535:666']),
]
# ---------------------------------------------------------------------------
# Scenario: anycast
# Same three prefixes announced with different AS paths and MEDs —
# simulates anycast competition (best-path selection testing).
# ---------------------------------------------------------------------------
_ANYCAST_ROUTES = [
# Anycast prefix 1 — two paths, different MED
_r('192.0.2.0/24', [65100, 65300], med=100),
# Anycast prefix 2 — longer AS path
_r('198.51.100.0/24', [65100, 65300, 65400], med=200),
# Anycast prefix 3 — shorter AS path, preferred
_r('203.0.113.0/24', [65100, 65200], med=50),
]
# ---------------------------------------------------------------------------
# Scenario: full_table
# 500+ prefixes simulating a large partial internet table.
# Built by expanding internet_sample with synthetic /24s.
# ---------------------------------------------------------------------------
def _gen_full_table():
routes = list(_INTERNET_V4) + list(_INTERNET_V6)
# Add synthetic /24 blocks from 100.x.x.0/24 space (RFC 6598)
transit_paths = [
[65100, 1299, 7922],
[65100, 3356, 16509],
[65100, 174, 15169],
[65100, 6461, 32934],
[65100, 2914, 8075],
[65100, 7018, 20940],
[65100, 1299, 54113],
[65100, 3356, 13335],
]
for i in range(100, 200):
for j in range(0, 256, 8):
path = transit_paths[((i - 100) + (j // 8)) % len(transit_paths)]
origin = 64512 + ((i * 32 + j // 8) % 1023)
routes.append(_r(f'100.{i}.{j}.0/24', path + [origin]))
return routes
# ---------------------------------------------------------------------------
# Scenario: lab_prefixes
# Mimics realistic enterprise/SP routes your lab routers would see.
# Useful for testing policy: communities, local-pref, AS path filtering.
# ---------------------------------------------------------------------------
_LAB_ROUTES = [
# Customer routes (shorter AS path, higher local-pref via community)
_r('10.200.0.0/24', [65100, 65500], communities=['65100:100'], local_pref=200),
_r('10.200.1.0/24', [65100, 65500], communities=['65100:100'], local_pref=200),
_r('10.200.2.0/24', [65100, 65500], communities=['65100:100'], local_pref=200),
# Peer routes (medium preference)
_r('10.201.0.0/22', [65100, 65600], communities=['65100:200'], local_pref=150),
_r('10.201.4.0/22', [65100, 65600], communities=['65100:200'], local_pref=150),
# Transit routes (longer path, lower preference)
_r('10.202.0.0/20', [65100, 1299, 65700], communities=['65100:300'], local_pref=100),
_r('10.202.16.0/20', [65100, 3356, 65700], communities=['65100:300'], local_pref=100),
# Default route
_r('0.0.0.0/0', [65100, 3356], communities=['65100:400']),
]
# ---------------------------------------------------------------------------
# Registry
# ---------------------------------------------------------------------------
SCENARIOS = {
'internet_sample': {
'description': 'Partial internet table (~80 IPv4 + 14 IPv6 prefixes with realistic AS paths)',
'routes': _INTERNET_V4 + _INTERNET_V6,
},
'churn': {
'description': '30 RFC documentation prefixes for announce/withdraw churn testing',
'routes': _CHURN_ROUTES,
},
'blackhole': {
'description': '5 /32 prefixes with RTBH community (65100:666 + 65535:666)',
'routes': _BLACKHOLE_ROUTES,
},
'anycast': {
'description': '3 prefixes with varying AS paths and MEDs for best-path testing',
'routes': _ANYCAST_ROUTES,
},
'full_table': {
'description': '500+ prefixes simulating a large partial internet table',
'routes': _gen_full_table(),
},
'lab_prefixes': {
'description': 'Enterprise/SP-style routes with communities and local-pref for policy testing',
'routes': _LAB_ROUTES,
},
}

67
exabgp/startup.sh Normal file
View File

@ -0,0 +1,67 @@
#!/bin/bash
set -e
LOCAL_IP=${EXABGP_LOCAL_IP:-10.40.40.202}
LOCAL_AS=${EXABGP_LOCAL_AS:-65100}
PEER_AS=${EXABGP_PEER_AS:-65020}
PEER_1=${EXABGP_PEER_1:-10.100.0.100}
PEER_2=${EXABGP_PEER_2:-10.100.0.200}
API_PORT=${EXABGP_API_PORT:-5050}
echo "================================================================"
echo " ExaBGP Route Injector"
echo " Local: ${LOCAL_IP} AS${LOCAL_AS}"
echo " Peers: ${PEER_1}, ${PEER_2} (AS${PEER_AS})"
echo " API: http://0.0.0.0:${API_PORT}"
echo "================================================================"
# Generate ExaBGP 5.x env file — ExaBGP looks here based on pip install prefix
mkdir -p /usr/local/etc/exabgp
exabgp env > /usr/local/etc/exabgp/exabgp.env
sed -i 's/drop = true/drop = false/' /usr/local/etc/exabgp/exabgp.env
sed -i 's/cli = true/cli = false/' /usr/local/etc/exabgp/exabgp.env
sed -i "s/destination = 'stdout'/destination = 'stderr'/" /usr/local/etc/exabgp/exabgp.env
# Generate exabgp.conf from environment
cat > /tmp/exabgp.conf << EOF
process api {
run /usr/local/bin/python3 /exabgp/api/server.py;
encoder text;
}
neighbor ${PEER_1} {
router-id ${LOCAL_IP};
local-address ${LOCAL_IP};
local-as ${LOCAL_AS};
peer-as ${PEER_AS};
description "CML-R9K-CORE-01";
hold-time 90;
family {
ipv4 unicast;
}
api {
processes [ api ];
}
}
neighbor ${PEER_2} {
router-id ${LOCAL_IP};
local-address ${LOCAL_IP};
local-as ${LOCAL_AS};
peer-as ${PEER_AS};
description "CML-R9K-CORE-02";
hold-time 90;
family {
ipv4 unicast;
}
api {
processes [ api ];
}
}
EOF
exec exabgp server /tmp/exabgp.conf

6
obmp-grafana/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.DS_Store
.idea
*.log
*.pyc
*.tgz
*.gz

26
obmp-grafana/LICENSE Normal file
View File

@ -0,0 +1,26 @@
Copyright 2021 Cisco Systems, Inc.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

12
obmp-grafana/README.md Normal file
View File

@ -0,0 +1,12 @@
# OpenBMP Grafana Integration
Grafana can be used with the OpenBMP PostgreSQL backend to visualize BGP data and statistics.
## Install/Run Grafana
Install/run grafana. See [grafana in openbmp compose](https://github.com/OpenBMP/obmp-docker/blob/main/docker-compose.yml) for
details on how you can run the Grafana container.
## Grafana Provisioning
This repo is structured to support [Grafana Provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/).
For the most part, this repo can be cloned directly in to the ```/etc/grafana/provisioning``` directory.

View File

@ -0,0 +1,237 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 1,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"gridPos": {
"h": 3,
"w": 24,
"x": 0,
"y": 0
},
"id": 6,
"links": [],
"options": {
"content": "# OpenBMP\n\n*Select a dashboard*",
"mode": "markdown"
},
"pluginVersion": "8.5.4",
"type": "text"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"gridPos": {
"h": 18,
"w": 4,
"x": 0,
"y": 3
},
"id": 7,
"links": [],
"options": {
"maxItems": 41,
"query": "",
"showHeadings": true,
"showRecentlyViewed": false,
"showSearch": true,
"showStarred": false,
"tags": [
"obmp-tops"
]
},
"pluginVersion": "8.5.4",
"tags": [],
"title": "Tops",
"type": "dashlist"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"gridPos": {
"h": 18,
"w": 5,
"x": 4,
"y": 3
},
"id": 8,
"links": [],
"options": {
"maxItems": 41,
"query": "",
"showHeadings": true,
"showRecentlyViewed": false,
"showSearch": true,
"showStarred": false,
"tags": [
"obmp-base"
]
},
"pluginVersion": "8.5.4",
"tags": [],
"title": "Base",
"type": "dashlist"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"gridPos": {
"h": 18,
"w": 5,
"x": 9,
"y": 3
},
"id": 4,
"links": [],
"options": {
"maxItems": 41,
"query": "",
"showHeadings": true,
"showRecentlyViewed": false,
"showSearch": true,
"showStarred": false,
"tags": [
"obmp-history"
]
},
"pluginVersion": "8.5.4",
"tags": [],
"title": "Prefix History",
"type": "dashlist"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"gridPos": {
"h": 18,
"w": 5,
"x": 14,
"y": 3
},
"id": 9,
"links": [],
"options": {
"maxItems": 41,
"query": "",
"showHeadings": true,
"showRecentlyViewed": false,
"showSearch": true,
"showStarred": false,
"tags": [
"obmp-l3vpn"
]
},
"pluginVersion": "8.5.4",
"tags": [],
"title": "L3VPN",
"type": "dashlist"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"gridPos": {
"h": 18,
"w": 5,
"x": 19,
"y": 3
},
"id": 3,
"links": [],
"options": {
"maxItems": 41,
"query": "",
"showHeadings": true,
"showRecentlyViewed": false,
"showSearch": true,
"showStarred": false,
"tags": [
"obmp-linkstate"
]
},
"pluginVersion": "8.5.4",
"tags": [],
"title": "Link State",
"type": "dashlist"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [],
"templating": {
"list": []
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "OBMP-Home",
"uid": "obmp-home",
"version": 1,
"weekStart": ""
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,506 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 4,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "none"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "up"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "light-green",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "down"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "light-red",
"mode": "fixed"
}
}
]
}
]
},
"gridPos": {
"h": 11,
"w": 4,
"x": 0,
"y": 0
},
"id": 3,
"links": [],
"maxDataPoints": 3,
"options": {
"displayLabels": [],
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"values": [
"value",
"percent"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n count(distinct router_hash_id) as value,\n r.state as metric\nFROM routers r\n JOIN bgp_peers p on (r.hash_id = p.router_hash_id)\n group by r.state\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Router Status",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 11,
"w": 20,
"x": 4,
"y": 0
},
"id": 1,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.5.4",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select max(r.timestamp) as timestamp,r.name,max(ip_address) as ip_address,max(r.state) as state,\n count(*) as peers,max(description) as description, CASE WHEN max(r.state) = 'up' THEN 1 ELSE 0 END as stateBool\n from routers r\n JOIN bgp_peers p on (r.hash_id = p.router_hash_id)\n GROUP BY r.name;",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Routers",
"type": "table"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "none"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "up"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "light-green",
"mode": "fixed"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "down"
},
"properties": [
{
"id": "color",
"value": {
"fixedColor": "light-red",
"mode": "fixed"
}
}
]
}
]
},
"gridPos": {
"h": 7,
"w": 4,
"x": 0,
"y": 11
},
"id": 4,
"links": [],
"maxDataPoints": 3,
"options": {
"displayLabels": [],
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"sortBy": "Value",
"sortDesc": true,
"values": [
"value",
"percent"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"last"
],
"fields": "",
"values": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "8.3.4",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n state as metric,\n count(*) value\nFROM bgp_peers\ngroup by metric",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Peer Status",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": []
},
"gridPos": {
"h": 14,
"w": 20,
"x": 4,
"y": 11
},
"id": 2,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": [
{
"desc": true,
"displayName": "IPv4 Prefixes"
}
]
},
"pluginVersion": "8.5.4",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": " SELECT\n max(RouterName) as \"RouterName\",\n max(PeerName) as \"PeerName\",\n max(PeerIP) as \"PeerIP\",\n max(PeerASN) as \"PeerASN\",\n max(peer_state) as \"State\",\n max(LastModified) as \"LastModified\",\n max(v4_prefixes) as \"IPv4 Prefixes\",\n max(v6_prefixes) as \"IPv6 Prefixes\",\n CASE WHEN max(peer_state) = 'up' THEN 1 ELSE 0 END as stateBool\nFROM v_peers p\n LEFT JOIN stats_peer_rib s ON (p.peer_hash_id = s.peer_hash_id\n AND s.interval_time >= now() - interval '20 minutes')\nGROUP BY p.peer_hash_id;\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Peers",
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-base"
],
"templating": {
"list": []
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "utc",
"title": "Inventory",
"uid": "inventory",
"version": 3,
"weekStart": ""
}

View File

@ -0,0 +1,910 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 5,
"iteration": 1654877090626,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"uid": "obmp-postgres"
},
"gridPos": {
"h": 3,
"w": 12,
"x": 0,
"y": 0
},
"id": 15,
"links": [],
"options": {
"content": "# Looking Glass\n\n> Enter an IP address with or without bit length. The longest matched prefix will be used. Specify the length to find exact match or best aggregate.",
"mode": "markdown"
},
"pluginVersion": "8.5.4",
"type": "text"
},
{
"circleMaxSize": "15",
"circleMinSize": 2,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"decimals": 0,
"esMetric": "Count",
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"hideEmpty": false,
"hideZero": false,
"id": 17,
"initialZoom": "1",
"locationData": "table",
"mapCenter": "(0°, 0°)",
"mapCenterLatitude": 0,
"mapCenterLongitude": 0,
"maxDataPoints": 1,
"mouseWheelZoom": false,
"showLegend": false,
"stickyLabels": false,
"tableQueryOptions": {
"geohashField": "geohash",
"labelField": "name",
"latitudeField": "latitude",
"longitudeField": "longitude",
"metricField": "value",
"queryType": "coordinates"
},
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n 10 as value, latitude, longitude, stateprov as name\nFROM geo_ip\nWHERE\n ip && '$input'\nORDER BY ip desc limit 1",
"refId": "A",
"select": [
[
{
"params": [
"latitude"
],
"type": "column"
}
]
],
"table": "v_ip_routes_geo",
"timeColumn": "lastmodified",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": "0,10",
"title": "Prefix Location",
"type": "grafana-worldmap-panel",
"unitPlural": "",
"unitSingle": "",
"valueName": "current"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"description": "Prefix found in router's RIB.",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 3
},
"id": 9,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "right",
"values": [
"value",
"percent"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"sum"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "time_series",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n floor(extract(epoch from max(r.timestamp))) as time,\n CASE WHEN v.router_hash_id is null THEN 'Not in Router RIB' ELSE 'In Router Rib' END as metric,\n 1 as value\nFROM routers r\n left join (select distinct router_hash_id\n from v_ip_routes \n where prefix = '$prefix'\n and iswithdrawn = false group by router_hash_id) v \n on (r.hash_id = v.router_hash_id)\nWHERE r.state = 'up'\nGROUP BY r.hash_id,v.router_hash_id\norder by time\n\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Router Visibility",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"description": "Prefix found in peer RIB's",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 6,
"y": 3
},
"id": 10,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "right",
"values": [
"value",
"percent"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"sum"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "time_series",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n floor(extract(epoch from max(p.timestamp))) as time,\n CASE WHEN v.peer_hash_id is null THEN 'Not in Peers RIB' ELSE 'In Peer RIB' END as metric,\n 1 as value\nFROM bgp_peers p\n left join (select peer_hash_id,isipv4\n from v_ip_routes \n where prefix = '$prefix' and prefix != '0.0.0.0/0'\n and iswithdrawn = false group by peer_hash_id,isipv4) v \n on (p.hash_id = v.peer_hash_id)\nWHERE p.isipv4 = CASE WHEN family('$prefix') = 4 THEN true ELSE false END\n AND p.state = 'up'\nGROUP BY p.hash_id,v.peer_hash_id,p.isipv4\norder by time\n\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Peer Visibility",
"type": "piechart"
},
{
"columns": [],
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fontSize": "100%",
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 8
},
"id": 12,
"links": [],
"scroll": true,
"showHeader": true,
"sort": {
"col": 0,
"desc": true
},
"styles": [
{
"alias": "Time",
"align": "auto",
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"pattern": "Time",
"type": "date"
},
{
"alias": "",
"align": "auto",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "raw_output",
"preserveFormat": true,
"sanitize": false,
"thresholds": [],
"type": "string",
"unit": "short"
},
{
"alias": "",
"align": "auto",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"decimals": 2,
"pattern": "/.*/",
"thresholds": [],
"type": "string",
"unit": "short"
}
],
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select distinct origin_as,i.as_name,org_id,org_name,remarks,address,city,state_prov,country,raw_output,source\n from ip_rib r LEFT JOIN info_asn i ON (i.asn = r.origin_as)\n where r.prefix = '$prefix' \n and origin_as > 0\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "ASN Info",
"transform": "table",
"type": "table-old"
},
{
"columns": [],
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fontSize": "100%",
"gridPos": {
"h": 4,
"w": 24,
"x": 0,
"y": 14
},
"id": 13,
"links": [],
"scroll": true,
"showHeader": true,
"sort": {
"col": 0,
"desc": true
},
"styles": [
{
"alias": "Time",
"align": "auto",
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"pattern": "Time",
"type": "date"
},
{
"alias": "",
"align": "auto",
"colorMode": "cell",
"colors": [
"#cca300",
"#e24d42",
"#9ac48a"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 0,
"mappingType": 1,
"pattern": "irr_origin_as",
"thresholds": [
"0",
"1"
],
"type": "number",
"unit": "none"
},
{
"alias": "",
"align": "auto",
"colorMode": "cell",
"colors": [
"#cca300",
"#e24d42",
"#9ac48a"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 0,
"mappingType": 1,
"pattern": "rpki_origin_as",
"thresholds": [
"0",
"1"
],
"type": "number",
"unit": "none"
},
{
"alias": "",
"align": "auto",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"decimals": 2,
"pattern": "/.*/",
"thresholds": [],
"type": "string",
"unit": "short"
}
],
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select distinct r.origin_as as recv_origin_as,\n CASE WHEN g.rpki_origin_as is null THEN 0 ELSE g.rpki_origin_as END as rpki_origin_as,\n CASE WHEN i.origin_as is null THEN 0 ELSE i.origin_as END as irr_origin_as,\n i.descr,i.source\n from ip_rib r \n LEFT JOIN info_route i ON (i.prefix = r.prefix)\n LEFT JOIN global_ip_rib g ON (g.prefix = r.prefix)\n where r.prefix = '$prefix'\n and r.origin_as > 0\n AND r.peer_hash_id in ($peer_hash)\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Prefix Info",
"transform": "table",
"type": "table-old"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"decimals": 0,
"displayName": "",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "locale"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "displayName",
"value": "Time"
},
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "prefix"
},
"properties": [
{
"id": "displayName",
"value": "Prefix"
},
{
"id": "unit",
"value": "short"
},
{
"id": "decimals",
"value": 2
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "Prefix History ",
"url": "/d/prefix-hist/prefix-history-by-prefix?orgId=1&var-prefix=${__value.text}&var-input=${__value.text}"
}
]
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "origin_as"
},
"properties": [
{
"id": "displayName",
"value": "Origin"
},
{
"id": "unit",
"value": "none"
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "ASN View",
"url": "d/asnview-agg/asn-view?orgId=1&var-asn_num=${__value.text}"
}
]
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "iswithdrawn"
},
"properties": [
{
"id": "displayName",
"value": "Withdrawn"
},
{
"id": "unit",
"value": "bool"
},
{
"id": "custom.displayMode",
"value": "color-background-solid"
},
{
"id": "custom.align",
"value": "auto"
},
{
"id": "color",
"value": {
"mode": "continuous-GrYlRd"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Time"
},
"properties": [
{
"id": "custom.width",
"value": 194
}
]
}
]
},
"gridPos": {
"h": 23,
"w": 24,
"x": 0,
"y": 18
},
"id": 3,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": []
},
"pluginVersion": "8.5.4",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select distinct ip.*, \n \tFIRST_VALUE(geo_ip.city) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as city,\n \tFIRST_VALUE(geo_ip.stateprov) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as stateprov,\n \tFIRST_VALUE(geo_ip.country) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as country,\n ls.local_router_name\n\tFROM (SELECT lastmodified,peername,prefix,\n \tiswithdrawn,origin_as,med,localpref,nh,as_path,communities\n from v_ip_routes\n \t\twhere prefix && '$input' and prefixlen <= 32 AND prefixlen > 0\n \t\t AND peer_hash_id in ($peer_hash)\n \t\tlimit 2000\n \t) ip\n\t\tLEFT JOIN geo_ip on (geo_ip.ip >>= ip.prefix AND geo_ip.ip != '0.0.0.0/0')\n LEFT JOIN v_ls_prefixes ls ON (ls.prefix >>= ip.nh and length(ls.local_router_name) > 0)",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Looking Glass",
"transformations": [
{
"id": "merge",
"options": {
"reducers": []
}
}
],
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-base"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "17.44.224.0/22",
"value": "17.44.224.0/22"
},
"hide": 0,
"label": "Prefix/IP",
"name": "input",
"options": [
{
"selected": false,
"text": "3.235.73.1",
"value": "3.235.73.1"
}
],
"query": "17.44.224.0/22",
"queryValue": "50.227.215.188",
"skipUrlSync": false,
"type": "textbox"
},
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name as __text, hash_id as __value from routers where state = 'up'",
"hide": 0,
"includeAll": true,
"label": "Router",
"multi": true,
"name": "router_hash",
"options": [],
"query": "select name as __text, hash_id as __value from routers where state = 'up'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash)",
"hide": 0,
"includeAll": true,
"label": "Peer",
"multi": true,
"name": "peer_hash",
"options": [],
"query": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": false,
"text": "17.44.224.0/22",
"value": "17.44.224.0/22"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select prefix from ip_rib \nwhere prefix >>= '$input' and peer_hash_id in ($peer_hash) \norder by prefix desc limit 1",
"hide": 2,
"includeAll": false,
"multi": false,
"name": "prefix",
"options": [],
"query": "select prefix from ip_rib \nwhere prefix >>= '$input' and peer_hash_id in ($peer_hash) \norder by prefix desc limit 1",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Looking Glass",
"uid": "lookingglass",
"version": 4,
"weekStart": ""
}

View File

@ -0,0 +1,644 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 7,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"links": [],
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "locale"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 11,
"x": 0,
"y": 0
},
"id": 1,
"links": [],
"options": {
"legend": {
"calcs": [
"mean",
"max",
"sum"
],
"displayMode": "table",
"placement": "right",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "8.0.5",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroup(interval_time, $__interval) as time,\n sum(updates) as updates, sum(withdraws) as withdraws\nFROM stats_chg_byasn s\nWHERE $__timeFilter(interval_time)\n AND s.origin_as = [[origin_as]]\n AND s.peer_hash_id in ($peer_hash)\n\ngroup by time\nORDER BY time ASC\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Prefix Advertisements & Withdrawals",
"type": "timeseries"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisCenteredZero": false,
"axisColorMode": "text",
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 5,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "never",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 0,
"links": [],
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "locale"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 13,
"x": 11,
"y": 0
},
"id": 2,
"links": [],
"options": {
"legend": {
"calcs": [
"mean",
"max",
"sum"
],
"displayMode": "table",
"placement": "right",
"showLegend": true
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "8.0.5",
"targets": [
{
"alias": "",
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroup(interval_time, $__interval) as time,\n sum(updates) as updates, sum(withdraws) as withdraws,\n left(PeerName,32) as metric\nFROM stats_chg_byasn l\n JOIN v_peers p ON (l.peer_hash_id = p.peer_hash_id)\nWHERE $__timeFilter(interval_time)\n AND l.origin_as = [[origin_as]]\n AND l.peer_hash_id in ($peer_hash)\n\nGROUP BY peername,time\nORDER BY time ASC\n\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Changes by Peer",
"type": "timeseries"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.width",
"value": 201
}
]
},
{
"matcher": {
"id": "byName",
"options": "origin_as"
},
"properties": [
{
"id": "custom.width",
"value": 118
}
]
},
{
"matcher": {
"id": "byName",
"options": "localpref"
},
"properties": [
{
"id": "custom.width",
"value": 76
}
]
},
{
"matcher": {
"id": "byName",
"options": "communities"
},
"properties": [
{
"id": "custom.width",
"value": 525
}
]
},
{
"matcher": {
"id": "byName",
"options": "as_path"
},
"properties": [
{
"id": "custom.width",
"value": 252
}
]
},
{
"matcher": {
"id": "byName",
"options": "med"
},
"properties": [
{
"id": "custom.width",
"value": 87
}
]
},
{
"matcher": {
"id": "byName",
"options": "nh"
},
"properties": [
{
"id": "custom.width",
"value": 136
}
]
},
{
"matcher": {
"id": "byName",
"options": "prefix"
},
"properties": [
{
"id": "custom.width",
"value": 206
}
]
},
{
"matcher": {
"id": "byName",
"options": "event"
},
"properties": [
{
"id": "custom.width",
"value": 113
}
]
},
{
"matcher": {
"id": "byName",
"options": "routername"
},
"properties": [
{
"id": "custom.width",
"value": 142
}
]
},
{
"matcher": {
"id": "byName",
"options": "peername"
},
"properties": [
{
"id": "custom.width",
"value": 283
}
]
}
]
},
"gridPos": {
"h": 20,
"w": 24,
"x": 0,
"y": 7
},
"id": 3,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": []
},
"pluginVersion": "9.1.7",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": " select lastmodified,event,routername,peername,prefix,nh,\n origin_as,localpref,med,as_path,communities\n from v_ip_routes_history \n where $__timeFilter(lastmodified)\n AND origin_as = [[origin_as]]\n AND peer_hash_id in ($peer_hash)\n\n order by lastmodified desc,id desc\n limit $limit;",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Prefix History",
"type": "table"
}
],
"refresh": "",
"schemaVersion": 37,
"style": "dark",
"tags": [
"obmp-history"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "16509",
"value": "16509"
},
"hide": 0,
"label": "Origin ASN",
"name": "origin_as",
"options": [
{
"selected": true,
"text": "0",
"value": "0"
}
],
"query": "0",
"skipUrlSync": false,
"type": "textbox"
},
{
"current": {
"selected": false,
"text": "1000",
"value": "1000"
},
"hide": 0,
"includeAll": false,
"label": "Limit",
"multi": false,
"name": "limit",
"options": [
{
"selected": true,
"text": "1000",
"value": "1000"
},
{
"selected": false,
"text": "5000",
"value": "5000"
},
{
"selected": false,
"text": "10000",
"value": "10000"
}
],
"query": "1000,5000,10000",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name as __text, hash_id as __value from routers",
"hide": 0,
"includeAll": true,
"label": "Router",
"multi": true,
"name": "router_hash",
"options": [],
"query": "select name as __text, hash_id as __value from routers",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) ",
"hide": 0,
"includeAll": true,
"label": "Peer",
"multi": true,
"name": "peer_hash",
"options": [],
"query": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) ",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
}
]
},
"time": {
"from": "now-24h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Prefix History (by ASN)",
"uid": "prefix-hist-asn",
"version": 2,
"weekStart": ""
}

View File

@ -0,0 +1,376 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 8,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "custom.width",
"value": 214
},
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
}
]
},
{
"matcher": {
"id": "byName",
"options": "localpref"
},
"properties": [
{
"id": "custom.width",
"value": 107
}
]
},
{
"matcher": {
"id": "byName",
"options": "med"
},
"properties": [
{
"id": "custom.width",
"value": 79
}
]
},
{
"matcher": {
"id": "byName",
"options": "nh"
},
"properties": [
{
"id": "custom.width",
"value": 122
}
]
},
{
"matcher": {
"id": "byName",
"options": "origin_as"
},
"properties": [
{
"id": "custom.width",
"value": 112
}
]
},
{
"matcher": {
"id": "byName",
"options": "prefix"
},
"properties": [
{
"id": "custom.width",
"value": 148
}
]
},
{
"matcher": {
"id": "byName",
"options": "routername"
},
"properties": [
{
"id": "custom.width",
"value": 216
}
]
},
{
"matcher": {
"id": "byName",
"options": "event"
},
"properties": [
{
"id": "custom.width",
"value": 112
}
]
},
{
"matcher": {
"id": "byName",
"options": "peername"
},
"properties": [
{
"id": "custom.width",
"value": 310
}
]
}
]
},
"gridPos": {
"h": 23,
"w": 23,
"x": 0,
"y": 0
},
"id": 3,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": []
},
"pluginVersion": "9.1.7",
"targets": [
{
"alias": "",
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": " select lastmodified,event,routername,peername,prefix,\n origin_as,nh,localpref,med,as_path,communities \n from v_ip_routes_history \n where $__timeFilter(lastmodified)\n AND peer_hash_id in ($peer_hash)\n AND communities && '{$community}'\n order by lastmodified desc,id desc\n limit $limit;",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Prefix History",
"type": "table"
}
],
"schemaVersion": 37,
"style": "dark",
"tags": [
"obmp-history"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "13445:1000",
"value": "13445:1000"
},
"hide": 0,
"includeAll": false,
"label": "Community",
"multi": false,
"name": "community",
"options": [
{
"selected": true,
"text": "13445:1000",
"value": "13445:1000"
}
],
"query": "13445:1000",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name as __text, hash_id as __value from routers",
"hide": 0,
"includeAll": true,
"label": "Router",
"multi": true,
"name": "router_hash",
"options": [],
"query": "select name as __text, hash_id as __value from routers",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash)",
"hide": 0,
"includeAll": true,
"label": "Peer",
"multi": true,
"name": "peer_hash",
"options": [],
"query": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {
"selected": false,
"text": "1000",
"value": "1000"
},
"hide": 0,
"includeAll": false,
"label": "Limit",
"multi": false,
"name": "limit",
"options": [
{
"selected": true,
"text": "1000",
"value": "1000"
},
{
"selected": false,
"text": "5000",
"value": "5000"
},
{
"selected": false,
"text": "10000",
"value": "10000"
}
],
"query": "1000, 5000, 10000",
"skipUrlSync": false,
"type": "custom"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Prefix History (by Community)",
"uid": "prefix-hist-comm",
"version": 2,
"weekStart": ""
}

View File

@ -0,0 +1,677 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"description": "",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 9,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"decimals": 0,
"displayName": "",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "none"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.width",
"value": 224
}
]
}
]
},
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 0
},
"id": 5,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": []
},
"pluginVersion": "9.1.7",
"targets": [
{
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select lastmodified,CASE WHEN iswithdrawn THEN 'Withdrawn' ELSE 'Active' END as state,routername,peername,prefix,prefixlen,\n origin_as,med,localpref,nh,as_path,communities \n from v_ip_routes \n where peer_hash_id in ($peer_hash) \n ${prefix_clause:raw}\n limit $limit\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Current RIB State",
"transformations": [
{
"id": "merge",
"options": {
"reducers": []
}
}
],
"type": "table"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"decimals": 0,
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 11,
"x": 0,
"y": 6
},
"hiddenSeries": false,
"id": 1,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"show": true,
"total": true,
"values": true
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "9.1.7",
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "",
"format": "time_series",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n interval_time as time,\n sum(updates) as updates, sum(withdraws) as withdraws\nFROM stats_chg_byprefix s\nWHERE $__timeFilter(interval_time)\n AND peer_hash_id in ($peer_hash)\n ${prefix_clause:raw}\n\ngroup by interval_time\nORDER BY interval_time ASC\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeRegions": [],
"title": "Prefix Advertisements & Withdrawals",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:289",
"format": "none",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:290",
"format": "short",
"logBase": 1,
"show": false
}
],
"yaxis": {
"align": false
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"decimals": 0,
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 13,
"x": 11,
"y": 6
},
"hiddenSeries": false,
"id": 2,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"sort": "total",
"sortDesc": true,
"total": true,
"values": true
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "9.1.7",
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "",
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n interval_time as time,\n sum(updates) + sum(withdraws) as value,\n left(PeerName,32) as metric\nFROM stats_chg_byprefix s\n JOIN v_peers p ON (s.peer_hash_id = p.peer_hash_id)\nWHERE $__timeFilter(interval_time)\n AND s.peer_hash_id in ($peer_hash)\n ${prefix_clause:raw}\n\nGROUP BY s.interval_time,peername\nORDER BY interval_time ASC\n\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeRegions": [],
"title": "Changes by Peer",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:346",
"decimals": 0,
"format": "none",
"label": "",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:347",
"format": "short",
"logBase": 1,
"show": false
}
],
"yaxis": {
"align": false
}
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "displayName",
"value": "Last Modified"
}
]
},
{
"matcher": {
"id": "byName",
"options": "Last Modified"
},
"properties": [
{
"id": "custom.width",
"value": 208
}
]
},
{
"matcher": {
"id": "byName",
"options": "communities"
},
"properties": [
{
"id": "custom.width",
"value": 402
}
]
}
]
},
"gridPos": {
"h": 20,
"w": 24,
"x": 0,
"y": 13
},
"id": 3,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": [
{
"desc": true,
"displayName": "Last Modified"
}
]
},
"pluginVersion": "9.1.7",
"targets": [
{
"alias": "",
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": " select lastmodified,event,routername,peername,prefix,nh,\n origin_as,as_path,communities,localpref,med\n from v_ip_routes_history \n where $__timeFilter(lastmodified)\n AND peer_hash_id in ($peer_hash)\n ${prefix_clause:raw}\n \n order by lastmodified desc,id desc\n limit $limit;",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Prefix History",
"type": "table"
}
],
"refresh": "",
"schemaVersion": 37,
"style": "dark",
"tags": [
"obmp-history"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name as __text, hash_id as __value from routers\n",
"hide": 0,
"includeAll": true,
"label": "Router",
"multi": true,
"name": "router_hash",
"options": [],
"query": "select name as __text, hash_id as __value from routers\n",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) ",
"hide": 0,
"includeAll": true,
"label": "Peer",
"multi": true,
"name": "peer_hash",
"options": [],
"query": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) ",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {
"selected": false,
"text": "1000",
"value": "1000"
},
"hide": 0,
"includeAll": false,
"label": "Limit",
"multi": false,
"name": "limit",
"options": [
{
"selected": true,
"text": "1000",
"value": "1000"
},
{
"selected": false,
"text": "5000",
"value": "5000"
},
{
"selected": false,
"text": "10000",
"value": "10000"
}
],
"query": "1000,5000,10000",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": false,
"text": "187.249.2.0/24",
"value": "187.249.2.0/24"
},
"hide": 0,
"label": "Prefix",
"name": "input",
"options": [
{
"selected": true,
"text": "187.249.2.0/24",
"value": "187.249.2.0/24"
}
],
"query": "187.249.2.0/24",
"queryValue": "67.211.53.0/24",
"skipUrlSync": false,
"type": "textbox"
},
{
"current": {
"selected": false,
"text": "AND prefix = '187.249.2.0/24'",
"value": "AND prefix = '187.249.2.0/24'"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select CASE WHEN '$input' != '-' THEN 'AND prefix = ''' || (select prefix from global_ip_rib where prefix >>= (CASE WHEN '$input' != '-' THEN '$input' ELSE '0/0' END)::inet order by prefix desc limit 1)::text || '''' ELSE '' END;",
"hide": 2,
"includeAll": false,
"multi": false,
"name": "prefix_clause",
"options": [],
"query": "select CASE WHEN '$input' != '-' THEN 'AND prefix = ''' || (select prefix from global_ip_rib where prefix >>= (CASE WHEN '$input' != '-' THEN '$input' ELSE '0/0' END)::inet order by prefix desc limit 1)::text || '''' ELSE '' END;",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
}
]
},
"time": {
"from": "now-3h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Prefix History (by Prefix)",
"uid": "prefix-hist",
"version": 2,
"weekStart": ""
}

View File

@ -0,0 +1,780 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 19,
"iteration": 1654877653557,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"description": "Prefix found in router's RIB.",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 0,
"y": 0
},
"id": 9,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "right",
"values": [
"value",
"percent"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"sum"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "time_series",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n floor(extract(epoch from max(r.timestamp))) as time,\n CASE WHEN v.router_hash_id is null THEN 'Not in Router RIB' ELSE 'In Router Rib' END as metric,\n 1 as value\nFROM routers r\n left join (select distinct router_hash_id\n from v_l3vpn_routes\n where prefix = '$prefix'\n and ('$rd' = '-' OR rd = '$rd')\n and iswithdrawn = false group by router_hash_id) v \n on (r.hash_id = v.router_hash_id)\nWHERE r.state = 'up'\nGROUP BY r.hash_id,v.router_hash_id\norder by time\n\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Router Visibility",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"description": "Prefix found in peer RIB's",
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 5,
"w": 6,
"x": 6,
"y": 0
},
"id": 10,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "right",
"values": [
"value",
"percent"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"sum"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "time_series",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n floor(extract(epoch from max(p.timestamp))) as time,\n CASE WHEN v.peer_hash_id is null THEN 'Not in Peers RIB' ELSE 'In Peer RIB' END as metric,\n 1 as value\nFROM bgp_peers p\n left join (select peer_hash_id,isipv4\n from l3vpn_rib \n where prefix = '$prefix' and prefix != '0.0.0.0/0'\n AND ('$rd' = '-' OR rd = '$rd')\n and iswithdrawn = false group by peer_hash_id,isipv4) v \n on (p.hash_id = v.peer_hash_id)\nWHERE p.isipv4 = CASE WHEN family('$prefix') = 4 THEN true ELSE false END\n AND p.state = 'up'\nGROUP BY p.hash_id,v.peer_hash_id,p.isipv4\norder by time\n\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Peer Visibility",
"type": "piechart"
},
{
"circleMaxSize": "15",
"circleMinSize": 2,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"decimals": 0,
"esMetric": "Count",
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 0
},
"hideEmpty": false,
"hideZero": false,
"id": 17,
"initialZoom": "1",
"locationData": "table",
"mapCenter": "(0°, 0°)",
"mapCenterLatitude": 0,
"mapCenterLongitude": 0,
"maxDataPoints": 1,
"mouseWheelZoom": false,
"showLegend": false,
"stickyLabels": false,
"tableQueryOptions": {
"geohashField": "geohash",
"labelField": "name",
"latitudeField": "latitude",
"longitudeField": "longitude",
"metricField": "value",
"queryType": "coordinates"
},
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n 10 as value, latitude, longitude, stateprov as name\nFROM geo_ip\nWHERE\n ip && '$input'\nORDER BY ip desc limit 1",
"refId": "A",
"select": [
[
{
"params": [
"latitude"
],
"type": "column"
}
]
],
"table": "v_ip_routes_geo",
"timeColumn": "lastmodified",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": "0,10",
"title": "Prefix Location",
"type": "grafana-worldmap-panel",
"unitPlural": "",
"unitSingle": "",
"valueName": "current"
},
{
"columns": [],
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fontSize": "100%",
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 8
},
"id": 12,
"links": [],
"scroll": true,
"showHeader": true,
"sort": {
"col": 0,
"desc": true
},
"styles": [
{
"alias": "Time",
"align": "auto",
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"pattern": "Time",
"type": "date"
},
{
"alias": "",
"align": "auto",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "raw_output",
"preserveFormat": true,
"sanitize": false,
"thresholds": [],
"type": "string",
"unit": "short"
},
{
"alias": "",
"align": "auto",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"decimals": 2,
"pattern": "/.*/",
"thresholds": [],
"type": "string",
"unit": "short"
}
],
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select distinct origin_as,i.as_name,org_id,org_name,remarks,address,city,state_prov,country,raw_output,source\n from l3vpn_rib r LEFT JOIN info_asn i ON (i.asn = r.origin_as)\n where r.prefix = '$prefix'\n and ('$rd' = '-' OR rd = '$rd')\n and origin_as > 0\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "ASN Info",
"transform": "table",
"type": "table-old"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"decimals": 0,
"displayName": "",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "locale"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "displayName",
"value": "Time"
},
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "prefix"
},
"properties": [
{
"id": "displayName",
"value": "Prefix"
},
{
"id": "unit",
"value": "short"
},
{
"id": "decimals",
"value": 2
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "Prefix History ",
"url": "/d/l3vpn-prefix-hist/prefix-history-by-prefix-l3vpn?orgId=1&var-input=${__value.text}&var-rd=$rd"
}
]
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "origin_as"
},
"properties": [
{
"id": "displayName",
"value": "Origin"
},
{
"id": "unit",
"value": "none"
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "ASN View",
"url": "/grafana/d/asnview/asn-view?orgId=1&var-asn_num=${__value.text}"
}
]
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "iswithdrawn"
},
"properties": [
{
"id": "displayName",
"value": "Withdrawn"
},
{
"id": "unit",
"value": "bool"
},
{
"id": "custom.displayMode",
"value": "color-background-solid"
},
{
"id": "custom.align",
"value": "auto"
},
{
"id": "color",
"value": {
"mode": "continuous-GrYlRd"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Time"
},
"properties": [
{
"id": "custom.width",
"value": 194
}
]
}
]
},
"gridPos": {
"h": 23,
"w": 24,
"x": 0,
"y": 14
},
"id": 3,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": []
},
"pluginVersion": "8.5.4",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select distinct ip.*, \n \tFIRST_VALUE(geo_ip.city) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as city,\n \tFIRST_VALUE(geo_ip.stateprov) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as stateprov,\n \tFIRST_VALUE(geo_ip.country) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as country,\n ls.local_router_name\n\tFROM (SELECT lastmodified,peername,rd,prefix,\n \tiswithdrawn,origin_as,med,localpref,nh,as_path,extcommunities,communities,largecommunities\n from v_l3vpn_routes\n \t\twhere prefix && '$input' \n \t\t AND peer_hash_id in ($peer_hash)\n \t\t AND ('$rd' = '-' OR rd = '$rd')\n \t\tlimit 2000\n \t) ip\n\t\tLEFT JOIN geo_ip on (geo_ip.ip >>= ip.prefix AND geo_ip.ip != '0.0.0.0/0')\n LEFT JOIN v_ls_prefixes ls ON (ls.prefix >>= ip.nh and length(ls.local_router_name) > 0)",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Looking Glass",
"transformations": [
{
"id": "merge",
"options": {
"reducers": []
}
}
],
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-l3vpn"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "80.0.0.2",
"value": "80.0.0.2"
},
"hide": 0,
"label": "Prefix/IP",
"name": "input",
"options": [
{
"selected": true,
"text": "80.0.0.2",
"value": "80.0.0.2"
}
],
"query": "80.0.0.2",
"queryValue": "50.227.215.188",
"skipUrlSync": false,
"type": "textbox"
},
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name as __text, hash_id as __value from routers where state = 'up'",
"hide": 0,
"includeAll": true,
"label": "Router",
"multi": true,
"name": "router_hash",
"options": [],
"query": "select name as __text, hash_id as __value from routers where state = 'up'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) and recvcapabilities like '% afi=1 safi=128 %';",
"hide": 0,
"includeAll": true,
"label": "Peer",
"multi": true,
"name": "peer_hash",
"options": [],
"query": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) and recvcapabilities like '% afi=1 safi=128 %';",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"isNone": true,
"selected": false,
"text": "None",
"value": ""
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select prefix from l3vpn_rib \nwhere prefix >>= '$input' and peer_hash_id in ($peer_hash) and ('$rd' = '-' OR rd = '$rd')\norder by prefix desc limit 1",
"hide": 2,
"includeAll": false,
"multi": false,
"name": "prefix",
"options": [],
"query": "select prefix from l3vpn_rib \nwhere prefix >>= '$input' and peer_hash_id in ($peer_hash) and ('$rd' = '-' OR rd = '$rd')\norder by prefix desc limit 1",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
},
{
"description": "RD in the format of N:N. Set to - for all.",
"hide": 2,
"label": "RD",
"name": "rd",
"query": "-",
"skipUrlSync": false,
"type": "constant"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Looking Glass - L3VPN",
"uid": "jiQW6VB7k",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,745 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"description": "",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 20,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"decimals": 0,
"displayName": "",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "none"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.width",
"value": 224
}
]
}
]
},
"gridPos": {
"h": 6,
"w": 24,
"x": 0,
"y": 0
},
"id": 5,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": []
},
"pluginVersion": "9.1.7",
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select lastmodified,CASE WHEN iswithdrawn THEN 'Withdrawn' ELSE 'Active' END as state,routername,peername,prefix,prefixlen,\n origin_as,med,localpref,nh,as_path,communities \n from v_l3vpn_routes \n where peer_hash_id in ($peer_hash) \n ${prefix_clause:raw}\n limit $limit\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Current RIB State",
"transformations": [
{
"id": "merge",
"options": {
"reducers": []
}
}
],
"type": "table"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"decimals": 0,
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 12,
"x": 0,
"y": 6
},
"hiddenSeries": false,
"id": 1,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"total": true,
"values": true
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "9.1.7",
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "time_series",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n interval_time as time,\n sum(updates) as updates, sum(withdraws) as withdraws\nFROM stats_l3vpn_chg_byprefix s\nWHERE $__timeFilter(interval_time)\n AND peer_hash_id in ($peer_hash)\n ${prefix_clause:raw}\n\ngroup by interval_time\nORDER BY interval_time ASC\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeRegions": [],
"title": "Prefix Advertisements & Withdrawals",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:289",
"format": "none",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:290",
"format": "short",
"logBase": 1,
"show": false
}
],
"yaxis": {
"align": false
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"decimals": 0,
"fieldConfig": {
"defaults": {
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 7,
"w": 12,
"x": 12,
"y": 6
},
"hiddenSeries": false,
"id": 2,
"legend": {
"alignAsTable": true,
"avg": true,
"current": false,
"max": true,
"min": false,
"rightSide": true,
"show": true,
"sort": "total",
"sortDesc": true,
"total": true,
"values": true
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {
"alertThreshold": true
},
"percentage": false,
"pluginVersion": "9.1.7",
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n interval_time as time,\n sum(updates) + sum(withdraws) as value,\n left(PeerName,32) as metric\nFROM stats_l3vpn_chg_byprefix s\n JOIN v_peers p ON (s.peer_hash_id = p.peer_hash_id)\nWHERE $__timeFilter(interval_time)\n AND s.peer_hash_id in ($peer_hash)\n ${prefix_clause:raw}\n\nGROUP BY s.interval_time,peername\nORDER BY interval_time ASC\n\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"thresholds": [],
"timeRegions": [],
"title": "Changes by Peer",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"mode": "time",
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:346",
"decimals": 0,
"format": "none",
"label": "",
"logBase": 1,
"show": true
},
{
"$$hashKey": "object:347",
"format": "short",
"logBase": 1,
"show": false
}
],
"yaxis": {
"align": false
}
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "displayName",
"value": "Last Modified"
}
]
},
{
"matcher": {
"id": "byName",
"options": "Last Modified"
},
"properties": [
{
"id": "custom.width",
"value": 208
}
]
},
{
"matcher": {
"id": "byName",
"options": "event"
},
"properties": [
{
"id": "mappings",
"value": [
{
"options": {
"Advertised": {
"color": "semi-dark-green",
"index": 0
},
"Withdrawn": {
"color": "semi-dark-red",
"index": 1
}
},
"type": "value"
}
]
},
{
"id": "custom.displayMode",
"value": "color-background-solid"
}
]
}
]
},
"gridPos": {
"h": 20,
"w": 24,
"x": 0,
"y": 13
},
"id": 3,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": [
{
"desc": true,
"displayName": "Last Modified"
}
]
},
"pluginVersion": "9.1.7",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": " select lastmodified,event,routername,peername,prefix,nh,\n origin_as,localpref,med,as_path,extcommunities,communities,largecommunities\n from v_l3vpn_routes_history \n where $__timeFilter(lastmodified)\n AND peer_hash_id in ($peer_hash)\n ${prefix_clause:raw}\n \n order by lastmodified desc,id desc\n limit $limit;",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Prefix History",
"type": "table"
}
],
"refresh": "",
"schemaVersion": 37,
"style": "dark",
"tags": [
"obmp-l3vpn"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select routername as __text, router_hash_id as __value from v_peers where recvcapabilities like '% afi=1 safi=128 %';\n",
"hide": 0,
"includeAll": true,
"label": "Router",
"multi": true,
"name": "router_hash",
"options": [],
"query": "select routername as __text, router_hash_id as __value from v_peers where recvcapabilities like '% afi=1 safi=128 %';\n",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) and recvcapabilities like '% afi=1 safi=128 %';",
"hide": 0,
"includeAll": true,
"label": "Peer",
"multi": true,
"name": "peer_hash",
"options": [],
"query": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) and recvcapabilities like '% afi=1 safi=128 %';",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {
"selected": false,
"text": "1000",
"value": "1000"
},
"hide": 0,
"includeAll": false,
"label": "Limit",
"multi": false,
"name": "limit",
"options": [
{
"selected": true,
"text": "1000",
"value": "1000"
},
{
"selected": false,
"text": "3000",
"value": "3000"
},
{
"selected": false,
"text": "5000",
"value": "5000"
},
{
"selected": false,
"text": "8000",
"value": "8000"
},
{
"selected": false,
"text": "10000",
"value": "10000"
}
],
"query": "1000,3000,5000,8000,10000",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": false,
"text": "100.100.100.100",
"value": "100.100.100.100"
},
"hide": 0,
"label": "Prefix",
"name": "input",
"options": [
{
"selected": true,
"text": "100.100.100.100",
"value": "100.100.100.100"
}
],
"query": "100.100.100.100",
"queryValue": "67.211.53.0/24",
"skipUrlSync": false,
"type": "textbox"
},
{
"current": {
"selected": false,
"text": "",
"value": ""
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select CASE WHEN '$input' != '-' THEN 'AND prefix = ''' || (\n select prefix from l3vpn_rib \n where peer_hash_id in ($peer_hash) \n AND ('$rd' = '-' OR rd = '$rd')\n AND prefix >>= (CASE WHEN '$input' != '-' THEN '$input' ELSE '0/0' END)::inet order by prefix desc limit 1)::text || ''''\n ELSE '' END;",
"hide": 2,
"includeAll": false,
"multi": false,
"name": "prefix_clause",
"options": [],
"query": "select CASE WHEN '$input' != '-' THEN 'AND prefix = ''' || (\n select prefix from l3vpn_rib \n where peer_hash_id in ($peer_hash) \n AND ('$rd' = '-' OR rd = '$rd')\n AND prefix >>= (CASE WHEN '$input' != '-' THEN '$input' ELSE '0/0' END)::inet order by prefix desc limit 1)::text || ''''\n ELSE '' END;",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
},
{
"current": {
"selected": false,
"text": "-",
"value": "-"
},
"description": "RD in the format of N:N. Set to - for all.",
"hide": 0,
"includeAll": false,
"label": "RD",
"multi": false,
"name": "rd",
"options": [
{
"selected": true,
"text": "-",
"value": "-"
}
],
"query": "-",
"skipUrlSync": false,
"type": "custom"
}
]
},
"time": {
"from": "now-3h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Prefix History (by Prefix) L3VPN",
"uid": "l3vpn-prefix-hist",
"version": 2,
"weekStart": ""
}

View File

@ -0,0 +1,702 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 21,
"iteration": 1654877634754,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"axisSoftMin": 0,
"fillOpacity": 80,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineWidth": 1,
"scaleDistribution": {
"type": "linear"
}
},
"decimals": 0,
"displayName": "Routes",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 11,
"x": 0,
"y": 0
},
"id": 5,
"options": {
"barRadius": 0,
"barWidth": 0.97,
"groupWidth": 0.7,
"legend": {
"calcs": [
"sum"
],
"displayMode": "table",
"placement": "bottom"
},
"orientation": "auto",
"showValue": "always",
"stacking": "none",
"tooltip": {
"mode": "single",
"sort": "none"
},
"xTickLabelRotation": 0,
"xTickLabelSpacing": 0
},
"pluginVersion": "8.3.4",
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select\n count(*) as count,\n rd\n from l3vpn_rib\n where\n peer_hash_id in ($peer_hash)\n and ('$rd' = '-' or rd = '$rd')\n and iswithdrawn = false\n group by rd\n",
"refId": "A",
"select": [
[
{
"params": [
"latitude"
],
"type": "column"
}
]
],
"table": "v_ip_routes_geo",
"timeColumn": "lastmodified",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Routes Advertised/Active",
"type": "barchart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"description": "",
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"axisSoftMin": 0,
"fillOpacity": 80,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineWidth": 1,
"scaleDistribution": {
"type": "linear"
}
},
"decimals": 0,
"displayName": "Routes",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "semi-dark-orange",
"value": null
}
]
},
"unit": "none"
},
"overrides": [
{
"__systemRef": "hideSeriesFrom",
"matcher": {
"id": "byNames",
"options": {
"mode": "exclude",
"names": [
"count"
],
"prefix": "All except:",
"readOnly": true
}
},
"properties": [
{
"id": "custom.hideFrom",
"value": {
"legend": false,
"tooltip": false,
"viz": true
}
}
]
}
]
},
"gridPos": {
"h": 8,
"w": 12,
"x": 11,
"y": 0
},
"id": 6,
"options": {
"barRadius": 0,
"barWidth": 0.97,
"groupWidth": 0.7,
"legend": {
"calcs": [
"sum"
],
"displayMode": "table",
"placement": "bottom"
},
"orientation": "auto",
"showValue": "always",
"stacking": "none",
"tooltip": {
"mode": "single",
"sort": "none"
},
"xTickLabelRotation": 0,
"xTickLabelSpacing": 0
},
"pluginVersion": "8.3.4",
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select\n count(*) as count,\n rd\n from l3vpn_rib\n where\n peer_hash_id in ($peer_hash)\n and ('$rd' = '-' OR rd = '$rd')\n and iswithdrawn = true\n group by rd\n",
"refId": "A",
"select": [
[
{
"params": [
"latitude"
],
"type": "column"
}
]
],
"table": "v_ip_routes_geo",
"timeColumn": "lastmodified",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Routes Withdrawn/Inactive",
"type": "barchart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"decimals": 0,
"displayName": "",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "locale"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "lastmodified"
},
"properties": [
{
"id": "displayName",
"value": "Time"
},
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "prefix"
},
"properties": [
{
"id": "displayName",
"value": "Prefix"
},
{
"id": "unit",
"value": "short"
},
{
"id": "decimals",
"value": 2
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "Prefix History ",
"url": "/d/l3vpn-prefix-hist/prefix-history-by-prefix-l3vpn?orgId=1&var-limit=$limit&var-input=${__value.text}&var-rd=$rd"
}
]
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "origin_as"
},
"properties": [
{
"id": "displayName",
"value": "Origin"
},
{
"id": "unit",
"value": "none"
},
{
"id": "links",
"value": [
{
"targetBlank": true,
"title": "ASN View",
"url": "/grafana/d/asnview/asn-view?orgId=1&var-asn_num=${__value.text}"
}
]
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "iswithdrawn"
},
"properties": [
{
"id": "displayName",
"value": "Withdrawn"
},
{
"id": "unit",
"value": "bool"
},
{
"id": "custom.displayMode",
"value": "color-background-solid"
},
{
"id": "custom.align",
"value": "auto"
},
{
"id": "color",
"value": {
"mode": "continuous-GrYlRd"
}
}
]
},
{
"matcher": {
"id": "byName",
"options": "Time"
},
"properties": [
{
"id": "custom.width",
"value": 194
}
]
}
]
},
"gridPos": {
"h": 23,
"w": 24,
"x": 0,
"y": 8
},
"id": 3,
"links": [],
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true,
"sortBy": []
},
"pluginVersion": "8.5.4",
"targets": [
{
"alias": "",
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select distinct ip.*, \n \tFIRST_VALUE(geo_ip.city) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as city,\n \tFIRST_VALUE(geo_ip.stateprov) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as stateprov,\n \tFIRST_VALUE(geo_ip.country) OVER (PARTITION BY ip.prefix ORDER BY geo_ip.ip DESC) as country,\n ls.local_router_name\n\tFROM (SELECT lastmodified,peername,rd,prefix,\n \tiswithdrawn,origin_as,med,localpref,nh,as_path,communities,extcommunities\n from v_l3vpn_routes\n \t\twhere \n \t\t peer_hash_id in ($peer_hash)\n \t\t AND ('$rd' = '-' OR rd = '$rd')\n \t\t AND (iswithdrawn in ($state))\n \t\tlimit $limit\n \t) ip\n\t\tLEFT JOIN geo_ip on (geo_ip.ip >>= ip.prefix AND geo_ip.ip != '0.0.0.0/0')\n LEFT JOIN v_ls_prefixes ls ON (ls.prefix >>= ip.nh and length(ls.local_router_name) > 0)",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Looking Glass (RD = $rd)",
"transformations": [
{
"id": "merge",
"options": {
"reducers": []
}
}
],
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-l3vpn"
],
"templating": {
"list": [
{
"current": {
"selected": true,
"text": [
"All"
],
"value": [
"$__all"
]
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select routername as __text, router_hash_id as __value from v_peers where router_state = 'up' and recvcapabilities like '% afi=1 safi=128 %';",
"hide": 0,
"includeAll": true,
"label": "Router",
"multi": true,
"name": "router_hash",
"options": [],
"query": "select routername as __text, router_hash_id as __value from v_peers where router_state = 'up' and recvcapabilities like '% afi=1 safi=128 %';",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"isNone": true,
"selected": false,
"text": "None",
"value": ""
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) and recvcapabilities like '% afi=1 safi=128 %';",
"hide": 0,
"includeAll": false,
"label": "Peer",
"multi": true,
"name": "peer_hash",
"options": [],
"query": "select peername as __text, peer_hash_id as __value from v_peers where router_hash_id in ($router_hash) and recvcapabilities like '% afi=1 safi=128 %';",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": true,
"text": "-",
"value": "-"
},
"description": "RD in the format of N:N. Set to - for all.",
"hide": 0,
"includeAll": false,
"label": "RD",
"multi": false,
"name": "rd",
"options": [
{
"selected": true,
"text": "-",
"value": "-"
}
],
"query": "-",
"queryValue": "203:20",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": true,
"text": "1000",
"value": "1000"
},
"hide": 0,
"includeAll": false,
"label": "Limit",
"multi": false,
"name": "limit",
"options": [
{
"selected": true,
"text": "1000",
"value": "1000"
},
{
"selected": false,
"text": "3000",
"value": "3000"
},
{
"selected": false,
"text": "5000",
"value": "5000"
},
{
"selected": false,
"text": "8000",
"value": "8000"
},
{
"selected": false,
"text": "10000",
"value": "10000"
}
],
"query": "1000,3000,5000,8000,10000",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": true,
"text": [
"Advertised"
],
"value": [
"False"
]
},
"hide": 0,
"includeAll": true,
"multi": true,
"name": "state",
"options": [
{
"selected": false,
"text": "All",
"value": "$__all"
},
{
"selected": true,
"text": "Advertised",
"value": "False"
},
{
"selected": false,
"text": "Withdrawn",
"value": "True"
}
],
"query": "Advertised : False, Withdrawn : True",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "L3VPN RIB Browser",
"uid": "v-cdzIBnz",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,324 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 14,
"iteration": 1654877691622,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"axisLabel": "",
"axisPlacement": "auto",
"barAlignment": 0,
"drawStyle": "line",
"fillOpacity": 10,
"gradientMode": "none",
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
},
"lineInterpolation": "linear",
"lineWidth": 1,
"pointSize": 6,
"scaleDistribution": {
"type": "linear"
},
"showPoints": "always",
"spanNulls": true,
"stacking": {
"group": "A",
"mode": "none"
},
"thresholdsStyle": {
"mode": "off"
}
},
"decimals": 0,
"links": [],
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "locale"
},
"overrides": []
},
"gridPos": {
"h": 8,
"w": 24,
"x": 0,
"y": 0
},
"id": 4,
"options": {
"legend": {
"calcs": [
"sum"
],
"displayMode": "table",
"placement": "right"
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"pluginVersion": "8.0.5",
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n $__timeGroup(timestamp, $__interval) as time,\n count(*) as count,\n CASE WHEN iswithdrawn THEN 'WITHDRAWN' ELSE 'ACTIVE' END as metric\nFROM\n ls_links_log\nWHERE\n $__timeFilter(timestamp)\n AND peer_hash_id = '$peer_hash'\nGROUP BY time,metric\nORDER BY time\n\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Link Changes",
"type": "timeseries"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "none"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "seq"
},
"properties": [
{
"id": "unit",
"value": "locale"
}
]
},
{
"matcher": {
"id": "byName",
"options": "timestamp"
},
"properties": [
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
}
]
},
{
"matcher": {
"id": "byName",
"options": "state"
},
"properties": [
{
"id": "custom.displayMode",
"value": "color-background-solid"
},
{
"id": "mappings",
"value": [
{
"options": {
"ACTIVE": {
"color": "semi-dark-green",
"index": 0
},
"WITHDRAWN": {
"color": "semi-dark-red",
"index": 1
}
},
"type": "value"
}
]
}
]
}
]
},
"gridPos": {
"h": 14,
"w": 24,
"x": 0,
"y": 8
},
"id": 2,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.5.4",
"targets": [
{
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT\n l.timestamp,\n l.seq,ln.name as LocalNode, rn.name as RemoteNode, l.mt_id,l.igp_metric,CASE WHEN l.iswithdrawn THEN 'WITHDRAWN' ELSE 'ACTIVE' END as state\nFROM\n ls_links_log l JOIN ls_nodes ln ON (l.local_node_hash_id = ln.hash_id and l.peer_hash_id = ln.peer_hash_id) \n JOIN ls_nodes rn ON (l.remote_node_hash_id = rn.hash_id and rn.peer_hash_id = l.peer_hash_id)\nWHERE\n $__timeFilter(l.timestamp)\n AND l.peer_hash_id = '$peer_hash'\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Link History",
"type": "table"
}
],
"refresh": false,
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-linkstate"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "yyz01-wxbb-crt01-lo0.webex.com",
"value": "367c22e4-57d9-2328-654b-96ea750e0267"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0",
"hide": 0,
"includeAll": false,
"label": "BGP Peer",
"multi": false,
"name": "peer_hash",
"options": [],
"query": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "LS Link History",
"uid": "-r7gAlyZk",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,479 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 15,
"iteration": 1654877712696,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 7,
"x": 0,
"y": 0
},
"id": 4,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "right",
"values": [
"value",
"percent"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"sum"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select floor(extract(epoch from max(timestamp))) as time,\n count(*) as value, CASE WHEN iswithdrawn THEN 'WITHDRAWN' ELSE 'ACTIVE' END as metric\nfrom ls_links\nwhere local_node_hash_id = '$local_node_hash_id'\n AND peer_hash_id = '$peer_hash'\ngroup by iswithdrawn\norder by time\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Link States",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 6,
"x": 7,
"y": 0
},
"id": 6,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "right",
"values": [
"value"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select floor(extract(epoch from max(timestamp))) as time,\n count(*) as value, CASE WHEN mt_id = 2 THEN 'IPv6' ELSE 'IPv4' END as metric\nfrom ls_links\nwhere local_node_hash_id = '$local_node_hash_id'\n AND peer_hash_id = '$peer_hash'\ngroup by mt_id\norder by time\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Links by Type",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"decimals": 0,
"displayName": "",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "none"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "timestamp"
},
"properties": [
{
"id": "displayName",
"value": "Time"
},
{
"id": "unit",
"value": "short"
},
{
"id": "decimals",
"value": 2
},
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "seq"
},
"properties": [
{
"id": "unit",
"value": "locale"
}
]
},
{
"matcher": {
"id": "byName",
"options": "state"
},
"properties": [
{
"id": "custom.displayMode",
"value": "color-background-solid"
},
{
"id": "mappings",
"value": [
{
"options": {
"ACTIVE": {
"color": "semi-dark-green",
"index": 0
},
"WITHDRAWN": {
"color": "semi-dark-red",
"index": 1
}
},
"type": "value"
}
]
}
]
}
]
},
"gridPos": {
"h": 14,
"w": 24,
"x": 0,
"y": 7
},
"id": 2,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.5.4",
"targets": [
{
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT state,local_router_name,local_igp_routerid,remote_router_name,remote_igp_routerid,mt_id,igp_metric,protocol, timestamp, seq\n FROM v_ls_links\n WHERE local_node_hash_id = '$local_node_hash_id'\n AND peer_hash_id = '$peer_hash'",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "$local_node_name Links",
"transformations": [
{
"id": "merge",
"options": {
"reducers": []
}
}
],
"transparent": true,
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-linkstate"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "yyz01-wxbb-crt01-lo0.webex.com",
"value": "367c22e4-57d9-2328-654b-96ea750e0267"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0\n ",
"hide": 0,
"includeAll": false,
"label": "BGP Peer",
"multi": false,
"name": "peer_hash",
"options": [],
"query": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0\n ",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": false,
"text": "AMS10-WXBB-CRT02",
"value": "1ed1da6b-6f57-57aa-92f5-edda59049e9a"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name as __text, hash_id as __value from ls_nodes where peer_hash_id = '$peer_hash' and not igp_router_id ~ '\\..[1-9A-F]00$'",
"hide": 0,
"includeAll": false,
"label": "ISIS Node",
"multi": false,
"name": "local_node_hash_id",
"options": [],
"query": "select name as __text, hash_id as __value from ls_nodes where peer_hash_id = '$peer_hash' and not igp_router_id ~ '\\..[1-9A-F]00$'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 5,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": false,
"text": "AMS10-WXBB-CRT02",
"value": "AMS10-WXBB-CRT02"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name from ls_nodes where hash_id = '$local_node_hash_id' and peer_hash_id = '$peer_hash'",
"hide": 2,
"includeAll": false,
"multi": false,
"name": "local_node_name",
"options": [],
"query": "select name from ls_nodes where hash_id = '$local_node_hash_id' and peer_hash_id = '$peer_hash'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "LS Links",
"uid": "MPqNG_sWz",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,511 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 16,
"iteration": 1654877745288,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"mappings": [
{
"options": {
"match": "null",
"result": {
"text": "N/A"
}
},
"type": "special"
}
],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "none"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 3,
"x": 0,
"y": 0
},
"id": 6,
"links": [],
"maxDataPoints": 100,
"options": {
"colorMode": "background",
"graphMode": "none",
"justifyMode": "auto",
"orientation": "horizontal",
"reduceOptions": {
"calcs": [
"sum"
],
"fields": "",
"values": false
},
"text": {},
"textMode": "auto"
},
"pluginVersion": "8.5.4",
"targets": [
{
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT count(*)\n FROM ls_nodes where peer_hash_id = '$peer_hash';",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Total Nodes",
"type": "stat"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "locale"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 7,
"x": 3,
"y": 0
},
"id": 8,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "right",
"values": [
"value"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"sum"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"format": "time_series",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select floor(extract(epoch from max(timestamp))) as time,\n count(*) as count, \n CASE WHEN iswithdrawn THEN 'WITHDRAWN' ELSE 'ACTIVE' END as metric\nfrom ls_links\nwhere peer_hash_id = '$peer_hash'\ngroup by iswithdrawn\norder by time\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Link States",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "locale"
},
"overrides": []
},
"gridPos": {
"h": 6,
"w": 7,
"x": 10,
"y": 0
},
"id": 9,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "right",
"values": [
"value"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"sum"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select floor(extract(epoch from max(timestamp))) as time,\n count(*) as count, \n CASE WHEN mt_id = 2 THEN 'IPv6' ELSE 'IPv4' END as metric\nfrom ls_links \nwhere peer_hash_id = '$peer_hash'\ngroup by metric\norder by time\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Links by Type",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"decimals": 0,
"displayName": "",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
},
"unit": "none"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "timestamp"
},
"properties": [
{
"id": "displayName",
"value": "Time"
},
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "state"
},
"properties": [
{
"id": "mappings",
"value": [
{
"options": {
"ACTIVE": {
"color": "semi-dark-green",
"index": 0
},
"WITHDRAWN": {
"color": "semi-dark-red",
"index": 1
}
},
"type": "value"
}
]
},
{
"id": "custom.displayMode",
"value": "color-background-solid"
}
]
}
]
},
"gridPos": {
"h": 13,
"w": 24,
"x": 0,
"y": 6
},
"id": 4,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.5.4",
"targets": [
{
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT state, nodename, routerid, protocol, timestamp, seq\n FROM v_ls_nodes\n where peer_hash_id = '$peer_hash'\n\n ",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Backbone ISIS Nodes",
"transformations": [
{
"id": "merge",
"options": {
"reducers": []
}
}
],
"type": "table"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"gridPos": {
"h": 2,
"w": 24,
"x": 0,
"y": 19
},
"id": 2,
"options": {
"content": "\n\n",
"mode": "markdown"
},
"pluginVersion": "8.5.4",
"type": "text"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-linkstate"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "yyz01-wxbb-crt01-lo0.webex.com",
"value": "367c22e4-57d9-2328-654b-96ea750e0267"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0\n",
"hide": 0,
"includeAll": false,
"label": "BGP Peer",
"multi": false,
"name": "peer_hash",
"options": [],
"query": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0\n",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "LS Nodes",
"uid": "dzdSWlyWz",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,377 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 17,
"iteration": 1654877763755,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "palette-classic"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"decimals": 0,
"mappings": [],
"unit": "short"
},
"overrides": []
},
"gridPos": {
"h": 7,
"w": 7,
"x": 0,
"y": 0
},
"id": 4,
"links": [],
"maxDataPoints": 3,
"options": {
"legend": {
"calcs": [],
"displayMode": "table",
"placement": "bottom",
"values": [
"value"
]
},
"pieType": "pie",
"reduceOptions": {
"calcs": [
"lastNotNull"
],
"fields": "",
"values": false
},
"tooltip": {
"mode": "single",
"sort": "none"
}
},
"targets": [
{
"format": "time_series",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select floor(extract(epoch from max(timestamp))) as time,\n count(*) as value, CASE WHEN mt_id = 2 THEN 'IPv6' ELSE 'IPv4' END as metric\nfrom v_ls_prefixes\nwhere local_node_hash_id = '$local_node_hash_id'\n and peer_hash_id = '$peer_hash'\ngroup by mt_id\n",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Prefixes Total by Type",
"type": "piechart"
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"fieldConfig": {
"defaults": {
"color": {
"mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"filterable": true,
"inspect": false
},
"decimals": 0,
"displayName": "",
"mappings": [],
"thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
},
{
"color": "red",
"value": 80
}
]
},
"unit": "none"
},
"overrides": [
{
"matcher": {
"id": "byName",
"options": "timestamp"
},
"properties": [
{
"id": "displayName",
"value": "Time"
},
{
"id": "unit",
"value": "time: YYYY-MM-DD HH:mm:ss.SSS"
},
{
"id": "custom.align"
}
]
},
{
"matcher": {
"id": "byName",
"options": "state"
},
"properties": [
{
"id": "custom.displayMode",
"value": "color-background-solid"
},
{
"id": "mappings",
"value": [
{
"options": {
"ACTIVE": {
"color": "semi-dark-green",
"index": 0
},
"WITHDRAWN": {
"color": "semi-dark-red",
"index": 1
}
},
"type": "value"
}
]
}
]
}
]
},
"gridPos": {
"h": 22,
"w": 23,
"x": 0,
"y": 7
},
"id": 2,
"options": {
"footer": {
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true
},
"pluginVersion": "8.5.4",
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"metricColumn": "none",
"rawQuery": true,
"rawSql": "SELECT state, mt_id, prefix, metric, protocol, timestamp\n FROM v_ls_prefixes\n WHERE local_node_hash_id = '$local_node_hash_id'\n AND peer_hash_id = '$peer_hash'",
"refId": "A",
"select": [
[
{
"params": [
"value"
],
"type": "column"
}
]
],
"timeColumn": "time",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "$local_node_name Prefixes",
"transformations": [
{
"id": "merge",
"options": {
"reducers": []
}
}
],
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-linkstate"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "yyz01-wxbb-crt01-lo0.webex.com",
"value": "367c22e4-57d9-2328-654b-96ea750e0267"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0",
"hide": 0,
"includeAll": false,
"label": "BGP Peer",
"multi": false,
"name": "peer_hash",
"options": [],
"query": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": false,
"text": "BLR03-WXBB-CRT01",
"value": "4275c83a-8d03-5361-d890-0b2bf4ad1b91"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name as __text, hash_id as __value from ls_nodes where peer_hash_id = '$peer_hash' AND not igp_router_id ~ '\\..[1-9A-F]00$'",
"hide": 0,
"includeAll": false,
"label": "ISIS Node",
"multi": false,
"name": "local_node_hash_id",
"options": [],
"query": "select name as __text, hash_id as __value from ls_nodes where peer_hash_id = '$peer_hash' AND not igp_router_id ~ '\\..[1-9A-F]00$'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 5,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": false,
"text": "BLR03-WXBB-CRT01",
"value": "BLR03-WXBB-CRT01"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select name from ls_nodes where hash_id = '$local_node_hash_id' and peer_hash_id = '$peer_hash'",
"hide": 2,
"includeAll": false,
"multi": false,
"name": "local_node_name",
"options": [],
"query": "select name from ls_nodes where hash_id = '$local_node_hash_id' and peer_hash_id = '$peer_hash'",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "LS Prefixes",
"uid": "iEcqtlyWk",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,216 @@
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "datasource",
"uid": "grafana"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"target": {
"limit": 100,
"matchAny": false,
"tags": [],
"type": "dashboard"
},
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": 23,
"iteration": 1654877522167,
"links": [],
"liveNow": false,
"panels": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"gridPos": {
"h": 28,
"w": 23,
"x": 0,
"y": 0
},
"id": 2,
"targets": [
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select local_node_hash_id as id,\n CASE WHEN max(local_router_name) = '' THEN max(local_igp_routerid) ELSE max(local_router_name) END as title,\n max(local_igp_routerid) as detail__routerid\n from v_ls_links\n where peer_hash_id = '$peer_hash'\n and local_igp_routerid like '%.0000' and remote_igp_routerid like '%.0000'\n and igp_metric < 16000000\n and state in ($state)\n and (local_node_hash_id = '$local_node_hash' or remote_node_hash_id = '$local_node_hash')\n group by local_node_hash_id\n order by title;",
"refId": " nodes",
"select": [
[
{
"params": [
"amr_rx_hbhloss_pct"
],
"type": "column"
}
]
],
"table": "as_path_metrics",
"timeColumn": "start_timestamp",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
},
{
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"format": "table",
"group": [],
"hide": false,
"metricColumn": "none",
"rawQuery": true,
"rawSql": "select local_node_hash_id || '->' || remote_node_hash_id as id,\n local_node_hash_id as source,\n remote_node_hash_id as target,\n max(igp_metric)::int as mainstat,\n max(state) as secondarystat,\n max(remote_router_name) as detail__remote\n from v_ls_links\n where peer_hash_id = '$peer_hash'\n and local_igp_routerid like '%.0000' and remote_igp_routerid like '%.0000'\n and igp_metric < 16000000\n and state in ($state)\n and (local_node_hash_id = '$local_node_hash' or remote_node_hash_id = '$local_node_hash')\ngroup by local_node_hash_id,remote_node_hash_id;\n ",
"refId": "edges",
"select": [
[
{
"params": [
"amr_rx_hbhloss_pct"
],
"type": "column"
}
]
],
"table": "as_path_metrics",
"timeColumn": "start_timestamp",
"timeColumnType": "timestamp",
"where": [
{
"name": "$__timeFilter",
"params": [],
"type": "macro"
}
]
}
],
"title": "Topology",
"type": "nodeGraph"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": [
"obmp-linkstate"
],
"templating": {
"list": [
{
"current": {
"selected": false,
"text": "yyz01-wxbb-crt01-lo0.webex.com",
"value": "367c22e4-57d9-2328-654b-96ea750e0267"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0",
"hide": 0,
"includeAll": false,
"label": "BGP Peer",
"multi": false,
"name": "peer_hash",
"options": [],
"query": "SELECT __text,__value FROM (\n select peername as __text, peer_hash_id as __value, count(*) as count\n from v_ls_nodes\n group by peername,peer_hash_id) d\nwhere count > 0",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
},
{
"current": {
"selected": true,
"text": "Active",
"value": "ACTIVE"
},
"hide": 0,
"includeAll": true,
"label": "State",
"multi": false,
"name": "state",
"options": [
{
"selected": false,
"text": "All",
"value": "$__all"
},
{
"selected": false,
"text": "Inactive",
"value": "WITHDRAWN"
},
{
"selected": true,
"text": "Active",
"value": "ACTIVE"
}
],
"query": "Inactive : WITHDRAWN, Active : ACTIVE",
"queryValue": "",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {
"selected": false,
"text": "NRT02-WXBB-CRT01",
"value": "3e96d517-e4b8-7264-1479-2814e9691f10"
},
"datasource": {
"type": "postgres",
"uid": "obmp_postgres"
},
"definition": "select local_router_name as __text, local_node_hash_id as __value \nfrom v_ls_links\nwhere peer_hash_id = '$peer_hash'\n and local_igp_routerid like '%.0000'\n and igp_metric < 16000000\n and state in ($state)\ngroup by local_router_name,local_node_hash_id",
"hide": 0,
"includeAll": false,
"label": "Node",
"multi": false,
"name": "local_node_hash",
"options": [],
"query": "select local_router_name as __text, local_node_hash_id as __value \nfrom v_ls_links\nwhere peer_hash_id = '$peer_hash'\n and local_igp_routerid like '%.0000'\n and igp_metric < 16000000\n and state in ($state)\ngroup by local_router_name,local_node_hash_id",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"type": "query"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "LinkState Topology",
"uid": "SNOLrQlnz",
"version": 3,
"weekStart": ""
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,125 @@
apiVersion: 1
providers:
# <string> an unique provider name. Required
- name: 'OpenBMP'
# <int> Org id. Default to 1
orgId: 1
# <string> name of the dashboard folder.
folder: ''
# <string> folder UID. will be automatically generated if not specified
folderUid: ''
# <string> provider type. Default to 'file'
type: file
# <bool> disable dashboard deletion
disableDeletion: false
# <int> how often Grafana will scan for changed dashboards
updateIntervalSeconds: 30
# <bool> allow updating provisioned dashboards from the UI
allowUiUpdates: true
options:
# <string, required> path to dashboard files on disk. Required when using the 'file' type
path: /var/lib/grafana/dashboards/General
# <bool> use folder names from filesystem to create folders in Grafana
foldersFromFilesStructure: false
# <string> an unique provider name. Required
- name: 'OpenBMP-Base'
# <int> Org id. Default to 1
orgId: 1
# <string> name of the dashboard folder.
folder: 'OBMP-Base'
# <string> folder UID. will be automatically generated if not specified
folderUid: '1001'
# <string> provider type. Default to 'file'
type: file
# <bool> disable dashboard deletion
disableDeletion: false
# <int> how often Grafana will scan for changed dashboards
updateIntervalSeconds: 30
# <bool> allow updating provisioned dashboards from the UI
allowUiUpdates: true
options:
# <string, required> path to dashboard files on disk. Required when using the 'file' type
path: /var/lib/grafana/dashboards/obmp/Base-1001
# <bool> use folder names from filesystem to create folders in Grafana
foldersFromFilesStructure: false
- name: 'OpenBMP-History'
# <int> Org id. Default to 1
orgId: 1
# <string> name of the dashboard folder.
folder: 'OBMP-History'
# <string> folder UID. will be automatically generated if not specified
folderUid: '1002'
# <string> provider type. Default to 'file'
type: file
# <bool> disable dashboard deletion
disableDeletion: false
# <int> how often Grafana will scan for changed dashboards
updateIntervalSeconds: 30
# <bool> allow updating provisioned dashboards from the UI
allowUiUpdates: true
options:
# <string, required> path to dashboard files on disk. Required when using the 'file' type
path: /var/lib/grafana/dashboards/obmp/History-1002
# <bool> use folder names from filesystem to create folders in Grafana
foldersFromFilesStructure: false
- name: 'OpenBMP-Tops'
# <int> Org id. Default to 1
orgId: 1
# <string> name of the dashboard folder.
folder: 'OBMP-Tops'
# <string> folder UID. will be automatically generated if not specified
folderUid: '1003'
# <string> provider type. Default to 'file'
type: file
# <bool> disable dashboard deletion
disableDeletion: false
# <int> how often Grafana will scan for changed dashboards
updateIntervalSeconds: 30
# <bool> allow updating provisioned dashboards from the UI
allowUiUpdates: true
options:
# <string, required> path to dashboard files on disk. Required when using the 'file' type
path: /var/lib/grafana/dashboards/obmp/Tops-1003
# <bool> use folder names from filesystem to create folders in Grafana
foldersFromFilesStructure: false
- name: 'OpenBMP-LinkState'
# <int> Org id. Default to 1
orgId: 1
# <string> name of the dashboard folder.
folder: 'OBMP-LinkState'
# <string> folder UID. will be automatically generated if not specified
folderUid: '1004'
# <string> provider type. Default to 'file'
type: file
# <bool> disable dashboard deletion
disableDeletion: false
# <int> how often Grafana will scan for changed dashboards
updateIntervalSeconds: 30
# <bool> allow updating provisioned dashboards from the UI
allowUiUpdates: true
options:
# <string, required> path to dashboard files on disk. Required when using the 'file' type
path: /var/lib/grafana/dashboards/obmp/LinkState-1004
# <bool> use folder names from filesystem to create folders in Grafana
foldersFromFilesStructure: false
- name: 'OpenBMP-L3VPN'
# <int> Org id. Default to 1
orgId: 1
# <string> name of the dashboard folder.
folder: 'OBMP-L3VPN'
# <string> folder UID. will be automatically generated if not specified
folderUid: '1005'
# <string> provider type. Default to 'file'
type: file
# <bool> disable dashboard deletion
disableDeletion: false
# <int> how often Grafana will scan for changed dashboards
updateIntervalSeconds: 30
# <bool> allow updating provisioned dashboards from the UI
allowUiUpdates: true
options:
# <string, required> path to dashboard files on disk. Required when using the 'file' type
path: /var/lib/grafana/dashboards/obmp/L3VPN-1005
# <bool> use folder names from filesystem to create folders in Grafana
foldersFromFilesStructure: false

View File

@ -0,0 +1,48 @@
# config file version
apiVersion: 1
# list of datasources that should be deleted from the database
#deleteDatasources:
# - name: PostgreSQL
# orgId: 1
# list of datasources to insert/update depending
# whats available in the database
datasources:
# <string, required> name of the datasource. Required
- name: PostgreSQL
uid: obmp_postgres
# <string, required> datasource type. Required
type: postgres
# <string, required> access mode. direct or proxy. Required
access: direct
# <int> org id. will default to orgId 1 if not specified
orgId: 1
# <string> url
url: obmp-psql:5432
# <string> database user, if used
user: openbmp
# <string> database name, if used
database: openbmp
# <bool> mark as default datasource. Max one per org
isDefault: true
secureJsonData:
password: openbmp
# <map> fields that will be converted to json and stored in json_data
jsonData:
tlsAuth: false
tlsAuthWithCACert: false
postgresVersion: 1200
sslmode: "require"
timescaledb: true
maxOpenConns: 6
maxIdleConns: 1
connMaxLifetime: 3600
# <string> json object of data that will be encrypted.
#secureJsonData:
version: 1
# <bool> allow users to edit datasources from the UI.
editable: true

View File

@ -3,75 +3,47 @@
# Copyright (c) 2021-2022 Cisco Systems, Inc. and others. All rights reserved. # Copyright (c) 2021-2022 Cisco Systems, Inc. and others. All rights reserved.
# #
# Build: # Build:
# DOCKER_BUILDKIT=1 docker build --platform linux/amd64 -t openbmp/postgres:2.1.1 . # DOCKER_BUILDKIT=1 docker build --platform linux/amd64 \
# --build-arg VERSION=2.2.1 \
# --squash \
# -t openbmp/postgres:2.2.1 .
# #
# Run: # Run:
# docker run --rm -it -p 5432:5432 \ # docker run --rm -it -p 5432:5432 \
# -e POSTGRES_PASSWORD=openbmp \ # -e POSTGRES_PASSWORD=openbmp \
# -e POSTGRES_USER=openbmp \ # -e POSTGRES_USER=openbmp \
# -e POSTGRES_DB=openbmp \ # -e POSTGRES_DB=openbmp \
# openbmp/postgres:2.1.0 # openbmp/postgres:2.2.1
# ----------------------------------------------- # -----------------------------------------------
# stage: Build # stage: Build
# ----------------------------------------------- # -----------------------------------------------
FROM timescale/timescaledb:2.6.0-pg14 AS build
ENV PG_CRON_VERSION 1.3.0
WORKDIR /ws
RUN apk update \
&& apk add --no-cache --virtual .build-deps build-base ca-certificates clang-dev llvm12 openssl
## Install pg_cron
RUN wget -O pg_cron.tgz https://github.com/citusdata/pg_cron/archive/v$PG_CRON_VERSION.tar.gz \
&& tar xvzf pg_cron.tgz && cd pg_cron-$PG_CRON_VERSION/ \
&& sed -i.bak -e 's/-Werror//g' Makefile \
&& sed -i.bak -e 's/-Wno-implicit-fallthrough//g' Makefile \
&& make
# ----------------------------------------------- # -----------------------------------------------
# stage: Final container # stage: Final container
# ----------------------------------------------- # -----------------------------------------------
FROM timescale/timescaledb-ha:pg14-ts2.8-latest
FROM timescale/timescaledb:2.6.0-pg14 ARG VERSION=0.0.0
ENV PG_CRON_VERSION 1.3.0 ENV PGDATA=/var/lib/postgresql/data
ENV PGDATA_TS=/var/lib/postgresql/ts/data
# Current/working dir
VOLUME ["/ws"]
WORKDIR /ws
# Expected data locations for base tables and timeseries # Expected data locations for base tables and timeseries
# #
VOLUME ["/var/lib/postgresql/data"] VOLUME ["/var/lib/postgresql/data"]
VOLUME ["/var/lib/postgresql/ts"] VOLUME ["/var/lib/postgresql/ts"]
ADD --chmod=755 scripts/004_obmp_psql_cfg.sh /docker-entrypoint-initdb.d/004_obmp_psql_cfg.sh
ADD --chmod=755 scripts/005_obmp_init.sh /docker-entrypoint-initdb.d/005_obmp_init.sh
RUN apk update \ USER root
&& apk add openssl\
&& openssl req -x509 -newkey rsa:4096 -nodes -subj "/C=US/ST=CA/L=Seattle/O=OpenBMP/CN=localhost" \
-keyout /psql_server.key -out /psql_server.crt -days 2048 \
&& chown postgres /psql_server.* \
&& mkdir -p /var/lib/postgresql/ts \
&& chown postgres /var/lib/postgresql/ts \
&& egrep -q -e '^hostssl( |\t)+all' /usr/local/share/postgresql/pg_hba.conf.sample || \
echo 'hostssl all all 0.0.0.0/0 md5' >> /usr/local/share/postgresql/pg_hba.conf.sample \
&& sed -i -e "s/^\#*listen_addresses.*=.*/listen_addresses = '*'/" /usr/local/share/postgresql/postgresql.conf.sample \
&& sed -i -e "s/^\#*max_wal_size.*=.*/max_wal_size = 10GB/" /usr/local/share/postgresql/postgresql.conf.sample \
&& sed -i -e "s/^\#*ssl[ ]*=.*/ssl = on/" /usr/local/share/postgresql/postgresql.conf.sample \
&& sed -i -e "s/^\#*ssl_cert_file.*=.*/ssl_cert_file = \'\/psql_server.crt\'/" /usr/local/share/postgresql/postgresql.conf.sample \
&& sed -i -e "s/^\#*ssl_key_file.*=.*/ssl_key_file = \'\/psql_server.key\'/" /usr/local/share/postgresql/postgresql.conf.sample \
&& sed -i -e "s/^shared_preload_libraries.*/shared_preload_libraries = 'timescaledb,pg_cron'/g" /usr/local/share/postgresql/postgresql.conf.sample
COPY --chmod=755 --from=build /ws/pg_cron-$PG_CRON_VERSION/pg_cron.so /usr/local/lib/postgresql/pg_cron.so RUN rm -rf /usr/lib/postgresql/12 /usr/lib/postgresql/13 \
COPY --chmod=644 --from=build /ws/pg_cron-$PG_CRON_VERSION/pg_cron.control /usr/local/share/postgresql/extension/pg_cron.control && mkdir -p /var/lib/postgresql/data /var/lib/postgresql/ts/data \
COPY --chmod=644 --from=build /ws/pg_cron-$PG_CRON_VERSION/pg_cron--1.0--1.1.sql /usr/local/share/postgresql/extension/pg_cron--1.0--1.1.sql && chown -R postgres /var/lib/postgresql/data /var/lib/postgresql/ts/data \
COPY --chmod=644 --from=build /ws/pg_cron-$PG_CRON_VERSION/pg_cron--1.1--1.2.sql /usr/local/share/postgresql/extension/pg_cron--1.1--1.2.sql && mkdir -p /usr/local/openbmp \
COPY --chmod=644 --from=build /ws/pg_cron-$PG_CRON_VERSION/pg_cron--1.2--1.3.sql /usr/local/share/postgresql/extension/pg_cron--1.2--1.3.sql && touch /usr/local/openbmp/version-${VERSION}
COPY --chmod=644 --from=build /ws/pg_cron-$PG_CRON_VERSION/pg_cron--1.0.sql /usr/local/share/postgresql/extension/pg_cron--1.0.sql
ADD --chmod=755 scripts/0_obmp_init.sh /docker-entrypoint-initdb.d/0_obmp_init.sh USER postgres
## providing db name for cron metadata tables
RUN echo "cron.database_name = 'openbmp'" >> /usr/local/share/postgresql/postgresql.conf.sample

View File

@ -0,0 +1,34 @@
#!/bin/bash
# OpenBMP Postgres configuration
#
# Copyright (c) 2022 Cisco Systems, Inc. and Tim Evens. All rights reserved.
#
# Create SSL cert
openssl req -x509 -newkey rsa:4096 -nodes -subj "/C=US/ST=CA/L=Seattle/O=OpenBMP/CN=localhost" \
-keyout $PGDATA/psql_server.key -out $PGDATA/psql_server.crt -days 2048 \
# Init timeseries location
mkdir -p $PGDATA_TS
chmod 0700 $PGDATA_TS
psql -U $POSTGRES_USER -c "CREATE TABLESPACE timeseries LOCATION '$PGDATA_TS';" $POSTGRES_DB
# Update postgres conf
sed -i -e "s/^\#*listen_addresses.*=.*/listen_addresses = '*'/" $PGDATA/postgresql.conf
sed -i -e "s/^\#*ssl[ ]*=.*/ssl = on/" $PGDATA/postgresql.conf
sed -i -e "s/^\#*ssl_cert_file.*=.*/ssl_cert_file = '${PGDATA//\//\\\/}\/psql_server.crt'/" $PGDATA/postgresql.conf
sed -i -e "s/^\#*ssl_key_file.*=.*/ssl_key_file = '${PGDATA//\//\\\/}\/psql_server.key'/" $PGDATA/postgresql.conf
sed -i -e "s/^shared_preload_libraries.*/shared_preload_libraries = 'timescaledb,pg_cron'/g" $PGDATA/postgresql.conf
echo "cron.database_name = 'openbmp'" >> $PGDATA/postgresql.conf
egrep -q -e '^hostssl( |\t)+all' $PGDATA/pg_hba.conf
if [[ $? ]]; then
echo 'hostssl all all 0.0.0.0/0 md5' >> $PGDATA/pg_hba.conf
fi
pg_ctl -D "$PGDATA" -m fast -w restart

View File

@ -0,0 +1,17 @@
#!/bin/bash
# postgres: Init script
#
# Copyright (c) 2021-2022 Cisco Systems, Inc. and Tim Evens. All rights reserved.
#
# >> NOTE, before adding extensions, required preload/config should be done first in 004_obmp_psql_cfg.sh
# Add extensions
psql -U $POSTGRES_USER -c "CREATE EXTENSION IF NOT EXISTS postgis CASCADE;" $POSTGRES_DB
psql -U $POSTGRES_USER -c "CREATE EXTENSION IF NOT EXISTS pgrouting CASCADE;" $POSTGRES_DB
# Add cron extension and config
psql -U $POSTGRES_USER -c "CREATE EXTENSION IF NOT EXISTS pg_cron;" $POSTGRES_DB
psql -U $POSTGRES_USER -c "GRANT USAGE ON SCHEMA cron TO $POSTGRES_USER;" $POSTGRES_DB

View File

@ -1,16 +0,0 @@
#!/bin/bash
# postgres: Init script
#
# Copyright (c) 2021 Cisco Systems, Inc. and Tim Evens. All rights reserved.
#
# Init timesries location
mkdir -p /var/lib/postgresql/ts/data
chmod 0700 /var/lib/postgresql/ts/data
psql -U $POSTGRES_USER -c "CREATE TABLESPACE timeseries LOCATION '/var/lib/postgresql/ts/data';" $POSTGRES_DB
# Config pg cron to database schema
psql -U $POSTGRES_USER -c "CREATE EXTENSION pg_cron;" $POSTGRES_DB
psql -U $POSTGRES_USER -c "GRANT USAGE ON SCHEMA cron TO $POSTGRES_USER;" $POSTGRES_DB

View File

@ -17,8 +17,8 @@
# Example docker build: # Example docker build:
# tar -c -C ../../ ./obmp-psql ./obmp-docker/psql-app ./obmp-java-api-message \ # tar -c -C ../../ ./obmp-psql ./obmp-docker/psql-app ./obmp-java-api-message \
# | docker buildx build --platform linux/amd64 --progress=plain \ # | docker buildx build --platform linux/amd64 --progress=plain \
# --build-arg VERSION=2.1.1 \ # --build-arg VERSION=2.2.2 \
# -f obmp-docker/psql-app/Dockerfile -t openbmp/psql-app:2.1.1 - # -f obmp-docker/psql-app/Dockerfile -t openbmp/psql-app:2.2.2 -
# ----------------------------------------------- # -----------------------------------------------
# stage: Build # stage: Build
@ -52,7 +52,8 @@ COPY --from=build /ws/database/ /usr/local/openbmp/database
COPY --chmod=755 --from=build /ws/cron_scripts/gen-whois/*.py /usr/local/openbmp/ COPY --chmod=755 --from=build /ws/cron_scripts/gen-whois/*.py /usr/local/openbmp/
COPY --chmod=755 --from=build /ws/cron_scripts/peeringdb/*.py /usr/local/openbmp/ COPY --chmod=755 --from=build /ws/cron_scripts/peeringdb/*.py /usr/local/openbmp/
COPY --chmod=755 --from=build /ws/cron_scripts/rpki/*.py /usr/local/openbmp/ COPY --chmod=755 --from=build /ws/cron_scripts/rpki/*.py /usr/local/openbmp/
COPY --chmod=755 --from=build /ws/scripts/dbip-to-psql.py /usr/local/openbmp/ COPY --chmod=755 --from=build /ws/scripts/geo-csv-to-psql.py /usr/local/openbmp/
COPY --chmod=755 --from=build /ws/scripts/db-ip-import.sh /usr/local/openbmp/
# Add files # Add files
ADD --chmod=755 obmp-docker/psql-app/scripts/run /usr/sbin/ ADD --chmod=755 obmp-docker/psql-app/scripts/run /usr/sbin/
@ -86,7 +87,7 @@ RUN apt-get update \
RUN cd /tmp && curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \ RUN cd /tmp && curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py \
&& python3 get-pip.py && python3 get-pip.py
RUN pip install ipaddr pykafka click RUN pip install ipaddr pykafka click netaddr
RUN pip3 install urllib3 requests RUN pip3 install urllib3 requests

View File

@ -74,10 +74,22 @@ initdb_postgres() {
if [[ ! -f /config/do_not_init_db ]]; then if [[ ! -f /config/do_not_init_db ]]; then
echo " ===> Initializing the DB" echo " ===> Initializing the DB"
echo "Waiting for postgres to start..."
done=0
while [ $done -eq 0 ]; do
psql -c "select 1;" > /dev/null 2>&1
if [[ $? -ne 0 ]]; then
echo " postgres not running, sleeping for 20 seconds..."
sleep 20
else
done=1
break
fi
done
# Load the schema files # Load the schema files
echo " ===> Loading Schemas" echo " ===> Loading Schemas"
echo "------" > /var/log/db_schema_load.log echo "------" > /var/log/db_schema_load.log
for file in $(ls -v /usr/local/openbmp/database/*.sql); do for file in $(ls -v /usr/local/openbmp/database/*.sql); do
echo " ===[ $file ] ========================================" >> /var/log/db_schema_load.log echo " ===[ $file ] ========================================" >> /var/log/db_schema_load.log
@ -110,12 +122,31 @@ enable_rpki() {
MAILTO="" MAILTO=""
# Update RPKI # Update RPKI
31 */2 * * * root . /usr/local/openbmp/pg_profile && /usr/local/openbmp/rpki_validator.py -u $PGUSER -p $PGPASSWORD -s $RPKI_URL --rpkipassword $RPKI_PASS --rpkiuser $RPKI_USER $PGHOST 31 */2 * * * root . /usr/local/openbmp/pg_profile && /usr/local/openbmp/rpki_validator.py -u $PGUSER -p $PGPASSWORD -s $RPKI_URL --rpkipassword $RPKI_PASS --rpkiuser $RPKI_USER $PGHOST > /var/log/cron-rpki-import.log
SETVAR SETVAR
} }
# -----------------------------------------------
# Enable DB-IP import
# -----------------------------------------------
enable_dbip() {
echo "===> Enabling DB-IP Import"
cat > /etc/cron.d/openbmp-dbip <<SETVAR
MAILTO=""
$(( $RANDOM % 59 + 1 )) $(( $RANDOM % 23 + 1 )) 1 * * root /usr/local/openbmp/db-ip-import.sh 2>&1 > /var/log/cron-dbip-import.log
SETVAR
# Load DB-IP on start
echo "Running DB-IP Import"
/usr/local/openbmp/db-ip-import.sh 2>/var/log/cron-dbip-import.log > /var/log/cron-dbip-import.log &
}
# ----------------------------------------------- # -----------------------------------------------
# Enable IRR # Enable IRR
# ----------------------------------------------- # -----------------------------------------------
@ -126,13 +157,13 @@ enable_irr() {
MAILTO="" MAILTO=""
# Update IRR # Update IRR
1 1 * * * root . /usr/local/openbmp/pg_profile && /usr/local/openbmp/gen_whois_route.py -u $PGUSER -p $PGPASSWORD $PGHOST > /var/log/irr_load.log 1 1 * * * root . /usr/local/openbmp/pg_profile && /usr/local/openbmp/gen_whois_route.py -u $PGUSER -p $PGPASSWORD $PGHOST 2>&1 > /var/log/irr_load.log
SETVAR SETVAR
# Load IRR data # Load IRR data
echo "Loading IRR data" echo "Loading IRR data"
/usr/local/openbmp/gen_whois_route.py -u $PGUSER -p $PGPASSWORD $PGHOST > /var/log/irr_load.log & /usr/local/openbmp/gen_whois_route.py -u $PGUSER -p $PGPASSWORD $PGHOST 2>/var/log/irr_load.log > /var/log/irr_load.log &
} }
# ----------------------------------------------- # -----------------------------------------------
@ -173,12 +204,43 @@ SETVAR
# Upgrade SQL # Upgrade SQL
# ----------------------------------------------- # -----------------------------------------------
upgrade() { upgrade() {
if [[ -f /config/do_not_init_db ]]; then
if [[ ! -f /config/psql-app-upgraded.2.1.0 ]]; then if [[ ! -f /config/psql-app-upgraded.2.1.0 ]]; then
echo "===> Upgrading to 2.1.0" echo "===> Upgrading to 2.1.0"
/tmp/upgrade/upgrade_2.1.0.sh /tmp/upgrade/upgrade_2.1.0.sh
touch /config/psql-app-upgraded.2.1.0 touch /config/psql-app-upgraded.2.1.0
echo "===> Done with upgrade" echo "===> Done with upgrade"
fi fi
if [[ ! -f /config/psql-app-upgraded.2.2.0 ]]; then
echo "===> Upgrading to 2.2.0"
/tmp/upgrade/upgrade_2.2.0.sh
touch /config/psql-app-upgraded.2.2.0
echo "===> Done with upgrade"
fi
if [[ ! -f /config/psql-app-upgraded.2.2.1 ]]; then
echo "===> Upgrading to 2.2.1"
/tmp/upgrade/upgrade_2.2.1.sh
touch /config/psql-app-upgraded.2.2.1
echo "===> Done with upgrade"
fi
if [[ ! -f /config/psql-app-upgraded.2.2.2 ]]; then
echo "===> Upgrading to 2.2.2"
/tmp/upgrade/upgrade_2.2.2.sh
touch /config/psql-app-upgraded.2.2.2
echo "===> Done with upgrade"
fi
else
touch /config/psql-app-upgraded.2.1.0
touch /config/psql-app-upgraded.2.2.0
touch /config/psql-app-upgraded.2.2.1
touch /config/psql-app-upgraded.2.2.2
fi
} }
@ -247,15 +309,20 @@ source /usr/local/openbmp/pg_profile
config_cron config_cron
rm -f /etc/cron.d/openbmp-rpki rm -f /etc/cron.d/openbmp-rpki
if [[ ${ENABLE_RPKI:-""} != "" ]]; then if [[ ${ENABLE_RPKI:-""} != "" && $ENABLE_RPKI == 1 ]]; then
enable_rpki enable_rpki
fi fi
rm -f /etc/cron.d/openbmp-irr rm -f /etc/cron.d/openbmp-irr
if [[ ${ENABLE_IRR:-""} != "" ]]; then if [[ ${ENABLE_IRR:-""} != "" && $ENABLE_IRR == 1 ]]; then
enable_irr enable_irr
fi fi
rm -f /etc/cron.d/openbmp-dbip
if [[ ${ENABLE_DBIP:-""} != "" && $ENABLE_DBIP == 1 ]]; then
enable_dbip
fi
initdb_postgres initdb_postgres

View File

@ -0,0 +1,15 @@
#!/bin/bash
# Upgrade script for L3VPN
#
# Copyright (c) 2022 Cisco Systems, Inc. and Tim Evens. All rights reserved.
#
# Author: Tim Evens <tim@evensweb.com>
#
source /usr/local/openbmp/pg_profile
echo "==> Upgrading to 2.2.0 SQL ==================================== "
psql < /tmp/upgrade/upgrade_2.2.0.sql
echo "==> Done upgrading to 2.2.0 SQL ================================== "

View File

@ -0,0 +1,186 @@
-- -----------------------------------------------------------------------
-- Copyright (c) 2022 Cisco Systems, Inc. and others. All rights reserved.
--
-- Ugrade from 2.1.0 to 2.2.0 changes
-- -----------------------------------------------------------------------
CREATE INDEX IF NOT EXISTS ip_rib_prefix_idx ON ip_rib (prefix);
CREATE OR REPLACE FUNCTION update_global_ip_rib(max_interval interval DEFAULT '2 hour')
RETURNS void AS $$
DECLARE
execution_start timestamptz := clock_timestamp();
insert_count int;
start_time timestamptz := now();
chg_prefix inet;
BEGIN
select time_bucket('5 minutes', timestamp - interval '5 minute') INTO start_time
FROM global_ip_rib order by timestamp desc limit 1;
IF start_time is null THEN
start_time = time_bucket('5 minutes', now() - max_interval);
raise INFO '-> Last query time is null, setting last query time within %', max_interval;
ELSIF start_time < now() - max_interval THEN
start_time = time_bucket('5 minutes', now() - max_interval);
raise INFO '-> Last query time is greater than max % time, setting last query time', max_interval;
ELSIF start_time > now() THEN
start_time = time_bucket('5 minutes', now() - interval '15 minutes');
raise INFO '-> Last query time is greater than current time, setting last query time to past 15 minutes';
END IF;
raise INFO 'Start time : %', execution_start;
raise INFO 'Last Query Time : %', start_time;
raise INFO '-> Looping through changed prefixes ...';
insert_count = 0;
FOR chg_prefix IN
SELECT prefix
FROM ip_rib_log WHERE timestamp >= start_time AND origin_as != 23456
UNION SELECT prefix FROM ip_rib where first_added_timestamp >= start_time
GROUP BY prefix
LOOP
insert_count = insert_count + 1;
INSERT INTO global_ip_rib (prefix,prefix_len,recv_origin_as,
iswithdrawn,timestamp,first_added_timestamp,num_peers,advertising_peers,withdrawn_peers)
SELECT r.prefix,
max(r.prefix_len),
r.origin_as,
bool_and(r.iswithdrawn) as isWithdrawn,
max(r.timestamp),
min(r.first_added_timestamp),
count(distinct r.peer_hash_id) as total_peers,
count(distinct r.peer_hash_id) FILTER (WHERE r.iswithdrawn = False) as advertising_peers,
count(distinct r.peer_hash_id) FILTER (WHERE r.iswithdrawn = True) as withdrawn_peers
FROM ip_rib r
WHERE r.prefix = chg_prefix
AND origin_as != 23456
GROUP BY r.prefix, r.origin_as
ON CONFLICT (prefix,recv_origin_as)
DO UPDATE SET timestamp=excluded.timestamp,
first_added_timestamp=excluded.first_added_timestamp,
iswithdrawn=excluded.iswithdrawn,
num_peers=excluded.num_peers,
advertising_peers=excluded.advertising_peers,
withdrawn_peers=excluded.withdrawn_peers;
END LOOP;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
-- Update IRR
raise INFO '-> Updating IRR info';
UPDATE global_ip_rib r SET
irr_origin_as=i.origin_as,
irr_source=i.source,
irr_descr=i.descr
FROM info_route i
WHERE r.timestamp >= start_time and i.prefix = r.prefix;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
-- Update RPKI entries - Limit query to only update what has changed in interval time
-- NOTE: The global_ip_rib table should have current times when first run (new table).
-- This will result in this query taking a while. After first run, it shouldn't take
-- as long.
raise INFO '-> Updating RPKI info';
UPDATE global_ip_rib r SET rpki_origin_as=p.origin_as
FROM rpki_validator p
WHERE r.timestamp >= start_time
AND p.prefix >>= r.prefix
AND r.prefix_len >= p.prefix_len
AND r.prefix_len <= p.prefix_len_max;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
END;
$$ LANGUAGE plpgsql;
drop view IF EXISTS v_ip_routes CASCADE;
CREATE VIEW v_ip_routes AS
SELECT CASE WHEN length(rtr.name) > 0 THEN rtr.name ELSE host(rtr.ip_address) END AS RouterName,
CASE WHEN length(p.name) > 0 THEN p.name ELSE host(p.peer_addr) END AS PeerName,
r.prefix AS Prefix,r.prefix_len AS PrefixLen,
attr.origin AS Origin,r.origin_as AS Origin_AS,attr.med AS MED,
attr.local_pref AS LocalPref,attr.next_hop AS NH,attr.as_path AS AS_Path,
attr.as_path_count AS ASPath_Count,attr.community_list AS Communities,
attr.ext_community_list AS ExtCommunities,attr.large_community_list AS LargeCommunities,
attr.cluster_list AS ClusterList,
attr.originator_id as Originator, attr.aggregator AS Aggregator,p.peer_addr AS PeerAddress, p.peer_as AS PeerASN,r.isIPv4 as isIPv4,
p.isIPv4 as isPeerIPv4, p.isL3VPNpeer as isPeerVPN,
r.timestamp AS LastModified, r.first_added_timestamp as FirstAddedTimestamp,
r.path_id, r.labels,
r.hash_id as rib_hash_id,
r.base_attr_hash_id as base_hash_id, r.peer_hash_id, rtr.hash_id as router_hash_id,r.isWithdrawn,
r.isPrePolicy,r.isAdjRibIn
FROM ip_rib r
JOIN bgp_peers p ON (r.peer_hash_id = p.hash_id)
JOIN base_attrs attr ON (attr.hash_id = r.base_attr_hash_id and attr.peer_hash_id = r.peer_hash_id)
JOIN routers rtr ON (p.router_hash_id = rtr.hash_id);
drop view IF EXISTS v_ip_routes_geo CASCADE;
CREATE VIEW v_ip_routes_geo AS
SELECT CASE WHEN length(rtr.name) > 0 THEN rtr.name ELSE host(rtr.ip_address) END AS RouterName,
CASE WHEN length(p.name) > 0 THEN p.name ELSE host(p.peer_addr) END AS PeerName,
r.prefix AS Prefix,r.prefix_len AS PrefixLen,
attr.origin AS Origin,r.origin_as AS Origin_AS,attr.med AS MED,
attr.local_pref AS LocalPref,attr.next_hop AS NH,attr.as_path AS AS_Path,
attr.as_path_count AS ASPath_Count,attr.community_list AS Communities,
attr.ext_community_list AS ExtCommunities,attr.large_community_list AS LargeCommunities,
attr.cluster_list AS ClusterList,attr.originator_id as Originator,
attr.aggregator AS Aggregator,p.peer_addr AS PeerAddress, p.peer_as AS PeerASN,r.isIPv4 as isIPv4,
p.isIPv4 as isPeerIPv4, p.isL3VPNpeer as isPeerVPN,
r.timestamp AS LastModified, r.first_added_timestamp as FirstAddedTimestamp,
r.path_id, r.labels,
r.hash_id as rib_hash_id,
r.base_attr_hash_id as base_hash_id, r.peer_hash_id, rtr.hash_id as router_hash_id,r.isWithdrawn,
r.isPrePolicy,r.isAdjRibIn,
g.ip as geo_ip,g.city as City, g.stateprov as stateprov, g.country as country,
g.latitude as latitude, g.longitude as longitude
FROM ip_rib r
JOIN bgp_peers p ON (r.peer_hash_id = p.hash_id)
JOIN base_attrs attr ON (attr.hash_id = r.base_attr_hash_id and attr.peer_hash_id = r.peer_hash_id)
JOIN routers rtr ON (p.router_hash_id = rtr.hash_id)
LEFT JOIN geo_ip g ON (g.ip && host(r.prefix)::inet)
WHERE r.isWithdrawn = false;
drop view IF EXISTS v_ip_routes_history CASCADE;
CREATE VIEW v_ip_routes_history AS
SELECT
CASE WHEN length(rtr.name) > 0 THEN rtr.name ELSE host(rtr.ip_address) END AS RouterName,
rtr.ip_address as RouterAddress,
CASE WHEN length(p.name) > 0 THEN p.name ELSE host(p.peer_addr) END AS PeerName,
log.prefix AS Prefix,log.prefix_len AS PrefixLen,
attr.origin AS Origin,log.origin_as AS Origin_AS,
attr.med AS MED,attr.local_pref AS LocalPref,attr.next_hop AS NH,
attr.as_path AS AS_Path,attr.as_path_count AS ASPath_Count,attr.community_list AS Communities,
attr.ext_community_list AS ExtCommunities,attr.large_community_list AS LargeCommunities,
attr.cluster_list AS ClusterList,attr.originator_id as Originator,
attr.aggregator AS Aggregator,p.peer_addr AS PeerIp,
p.peer_as AS PeerASN, p.isIPv4 as isPeerIPv4, p.isL3VPNpeer as isPeerVPN,
log.id,log.timestamp AS LastModified,
CASE WHEN log.iswithdrawn THEN 'Withdrawn' ELSE 'Advertised' END as event,
log.base_attr_hash_id as base_attr_hash_id, log.peer_hash_id, rtr.hash_id as router_hash_id
FROM ip_rib_log log
JOIN base_attrs attr
ON (log.base_attr_hash_id = attr.hash_id AND
log.peer_hash_id = attr.peer_hash_id)
JOIN bgp_peers p ON (log.peer_hash_id = p.hash_id)
JOIN routers rtr ON (p.router_hash_id = rtr.hash_id);

View File

@ -0,0 +1,15 @@
#!/bin/bash
# Upgrade script for L3VPN
#
# Copyright (c) 2022 Cisco Systems, Inc. and Tim Evens. All rights reserved.
#
# Author: Tim Evens <tim@evensweb.com>
#
source /usr/local/openbmp/pg_profile
echo "==> Upgrading to 2.2.1 SQL ==================================== "
psql < /tmp/upgrade/upgrade_2.2.1.sql
echo "==> Done upgrading to 2.2.1 SQL ================================== "

View File

@ -0,0 +1,188 @@
-- -----------------------------------------------------------------------
-- Copyright (c) 2022 Cisco Systems, Inc. and others. All rights reserved.
--
-- Ugrade from 2.2.0 to 2.2.1 changes
-- -----------------------------------------------------------------------
CREATE INDEX IF NOT EXISTS ip_rib_first_added_timestamp_idx ON ip_rib (first_added_timestamp DESC);
CREATE OR REPLACE FUNCTION update_global_ip_rib(max_interval interval DEFAULT '2 hour')
RETURNS void AS $$
DECLARE
execution_start timestamptz := clock_timestamp();
insert_count int;
start_time timestamptz := now();
chg_prefix inet;
BEGIN
select time_bucket('5 minutes', timestamp - interval '5 minute') INTO start_time
FROM global_ip_rib order by timestamp desc limit 1;
IF start_time is null THEN
start_time = time_bucket('5 minutes', now() - max_interval);
raise INFO '-> Last query time is null, setting last query time within %', max_interval;
ELSIF start_time < now() - max_interval THEN
start_time = time_bucket('5 minutes', now() - max_interval);
raise INFO '-> Last query time is greater than max % time, setting last query time', max_interval;
ELSIF start_time > now() THEN
start_time = time_bucket('5 minutes', now() - interval '15 minutes');
raise INFO '-> Last query time is greater than current time, setting last query time to past 15 minutes';
END IF;
raise INFO 'Start time : %', execution_start;
raise INFO 'Last Query Time : %', start_time;
raise INFO '-> Looping through changed prefixes ...';
insert_count = 0;
FOR chg_prefix IN
SELECT prefix
FROM ip_rib_log WHERE timestamp >= start_time AND origin_as != 23456
UNION SELECT prefix FROM ip_rib where first_added_timestamp >= start_time
GROUP BY prefix
LOOP
insert_count = insert_count + 1;
INSERT INTO global_ip_rib (prefix,prefix_len,recv_origin_as,
iswithdrawn,timestamp,first_added_timestamp,num_peers,advertising_peers,withdrawn_peers)
SELECT r.prefix,
max(r.prefix_len),
r.origin_as,
bool_and(r.iswithdrawn) as isWithdrawn,
max(r.timestamp),
min(r.first_added_timestamp),
count(distinct r.peer_hash_id) as total_peers,
count(distinct r.peer_hash_id) FILTER (WHERE r.iswithdrawn = False) as advertising_peers,
count(distinct r.peer_hash_id) FILTER (WHERE r.iswithdrawn = True) as withdrawn_peers
FROM ip_rib r
WHERE r.prefix = chg_prefix
AND origin_as != 23456
GROUP BY r.prefix, r.origin_as
ON CONFLICT (prefix,recv_origin_as)
DO UPDATE SET timestamp=excluded.timestamp,
first_added_timestamp=excluded.first_added_timestamp,
iswithdrawn=excluded.iswithdrawn,
num_peers=excluded.num_peers,
advertising_peers=excluded.advertising_peers,
withdrawn_peers=excluded.withdrawn_peers;
END LOOP;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
-- Update IRR
raise INFO '-> Updating IRR info';
UPDATE global_ip_rib r SET
irr_origin_as=i.origin_as,
irr_source=i.source,
irr_descr=i.descr
FROM info_route i
WHERE r.timestamp >= start_time and i.prefix = r.prefix;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
-- Update RPKI entries - Limit query to only update what has changed in interval time
-- NOTE: The global_ip_rib table should have current times when first run (new table).
-- This will result in this query taking a while. After first run, it shouldn't take
-- as long.
raise INFO '-> Updating RPKI info';
UPDATE global_ip_rib r SET rpki_origin_as=p.origin_as
FROM rpki_validator p
WHERE r.timestamp >= start_time
AND p.prefix >>= r.prefix
AND r.prefix_len >= p.prefix_len
AND r.prefix_len <= p.prefix_len_max;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION sync_global_ip_rib()
RETURNS void AS $$
DECLARE
execution_start timestamptz := clock_timestamp();
insert_count int;
start_time timestamptz := now();
BEGIN
raise INFO 'Start time : %', execution_start;
INSERT INTO global_ip_rib (prefix,prefix_len,recv_origin_as,
iswithdrawn,timestamp,first_added_timestamp,num_peers,advertising_peers,withdrawn_peers)
SELECT r.prefix,
max(r.prefix_len),
r.origin_as,
bool_and(r.iswithdrawn) as isWithdrawn,
max(r.timestamp),
min(r.first_added_timestamp),
count(distinct r.peer_hash_id) as total_peers,
count(distinct r.peer_hash_id) FILTER (WHERE r.iswithdrawn = False) as advertising_peers,
count(distinct r.peer_hash_id) FILTER (WHERE r.iswithdrawn = True) as withdrawn_peers
FROM ip_rib r
WHERE origin_as != 23456
GROUP BY r.prefix, r.origin_as
ON CONFLICT (prefix,recv_origin_as)
DO UPDATE SET timestamp=excluded.timestamp,
first_added_timestamp=excluded.first_added_timestamp,
iswithdrawn=excluded.iswithdrawn,
num_peers=excluded.num_peers,
advertising_peers=excluded.advertising_peers,
withdrawn_peers=excluded.withdrawn_peers;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
-- Update IRR
raise INFO '-> Updating IRR info';
UPDATE global_ip_rib r SET
irr_origin_as=i.origin_as,
irr_source=i.source,
irr_descr=i.descr
FROM info_route i
WHERE i.prefix = r.prefix;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
-- Update RPKI entries - Limit query to only update what has changed in interval time
-- NOTE: The global_ip_rib table should have current times when first run (new table).
-- This will result in this query taking a while. After first run, it shouldn't take
-- as long.
raise INFO '-> Updating RPKI info';
UPDATE global_ip_rib r SET rpki_origin_as=p.origin_as
FROM rpki_validator p
WHERE
p.prefix >>= r.prefix
AND r.prefix_len >= p.prefix_len
AND r.prefix_len <= p.prefix_len_max;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
END;
$$ LANGUAGE plpgsql;

View File

@ -0,0 +1,15 @@
#!/bin/bash
# Upgrade script for L3VPN
#
# Copyright (c) 2022 Cisco Systems, Inc. and Tim Evens. All rights reserved.
#
# Author: Tim Evens <tim@evensweb.com>
#
source /usr/local/openbmp/pg_profile
echo "==> Upgrading to 2.2.2 SQL ==================================== "
psql < /tmp/upgrade/upgrade_2.2.2.sql
echo "==> Done upgrading to 2.2.2 SQL ================================== "

View File

@ -0,0 +1,131 @@
-- -----------------------------------------------------------------------
-- Copyright (c) 2022 Cisco Systems, Inc. and others. All rights reserved.
--
-- Upgrade form 2.2.1 to 2.2.2
-- -----------------------------------------------------------------------
CREATE INDEX IF NOT EXISTS base_attrs_next_hop_idx ON base_attrs (next_hop);
CREATE OR REPLACE FUNCTION update_global_ip_rib(max_interval interval DEFAULT '2 hour')
RETURNS void AS $$
DECLARE
execution_start timestamptz := clock_timestamp();
insert_count int;
start_time timestamptz := now();
BEGIN
select time_bucket('5 minutes', timestamp - interval '5 minute') INTO start_time
FROM global_ip_rib order by timestamp desc limit 1;
IF start_time is null THEN
start_time = time_bucket('5 minutes', now() - max_interval);
raise INFO '-> Last query time is null, setting last query time within %', max_interval;
ELSIF start_time < now() - max_interval THEN
start_time = time_bucket('5 minutes', now() - max_interval);
raise INFO '-> Last query time is greater than max % time, setting last query time', max_interval;
ELSIF start_time > now() THEN
start_time = time_bucket('5 minutes', now() - interval '15 minutes');
raise INFO '-> Last query time is greater than current time, setting last query time to past 15 minutes';
END IF;
raise INFO 'Start time : %', execution_start;
raise INFO 'Last Query Time : %', start_time;
raise INFO '-> Updating changed prefixes ...';
insert_count = 0;
INSERT INTO global_ip_rib (prefix,prefix_len,recv_origin_as,
iswithdrawn,timestamp,first_added_timestamp,num_peers,advertising_peers,withdrawn_peers)
SELECT r.prefix,
max(r.prefix_len),
r.origin_as,
bool_and(r.iswithdrawn) as isWithdrawn,
max(r.timestamp),
min(r.first_added_timestamp),
count(distinct r.peer_hash_id) as total_peers,
count(distinct r.peer_hash_id) FILTER (WHERE r.iswithdrawn = False) as advertising_peers,
count(distinct r.peer_hash_id) FILTER (WHERE r.iswithdrawn = True) as withdrawn_peers
FROM ip_rib r
WHERE
(timestamp >= start_time OR first_added_timestamp >= start_time)
AND origin_as != 23456
GROUP BY r.prefix, r.origin_as
ON CONFLICT (prefix,recv_origin_as)
DO UPDATE SET timestamp=excluded.timestamp,
first_added_timestamp=excluded.first_added_timestamp,
iswithdrawn=excluded.iswithdrawn,
num_peers=excluded.num_peers,
advertising_peers=excluded.advertising_peers,
withdrawn_peers=excluded.withdrawn_peers;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
-- Update IRR
raise INFO '-> Updating IRR info';
UPDATE global_ip_rib r SET
irr_origin_as=i.origin_as,
irr_source=i.source,
irr_descr=i.descr
FROM info_route i
WHERE r.timestamp >= start_time and i.prefix = r.prefix;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
-- Update RPKI entries - Limit query to only update what has changed in interval time
-- NOTE: The global_ip_rib table should have current times when first run (new table).
-- This will result in this query taking a while. After first run, it shouldn't take
-- as long.
raise INFO '-> Updating RPKI info';
UPDATE global_ip_rib r SET rpki_origin_as=p.origin_as
FROM rpki_validator p
WHERE r.timestamp >= start_time
AND p.prefix >>= r.prefix
AND r.prefix_len >= p.prefix_len
AND r.prefix_len <= p.prefix_len_max;
GET DIAGNOSTICS insert_count = row_count;
raise INFO 'Rows updated : %', insert_count;
raise INFO 'Duration : %', clock_timestamp() - execution_start;
raise INFO 'Completion time: %', clock_timestamp();
END;
$$ LANGUAGE plpgsql;
drop view IF EXISTS v_ls_links CASCADE;
ALTER TABLE ls_links
ALTER COLUMN admin_group TYPE bigint,
ALTER COLUMN unreserved_bw TYPE varchar(128);
ALTER TABLE ls_links_log
ALTER COLUMN admin_group TYPE bigint,
ALTER COLUMN unreserved_bw TYPE varchar(128);
CREATE VIEW v_ls_links AS
SELECT localn.name as Local_Router_Name,remoten.name as Remote_Router_Name,
localn.igp_router_id as Local_IGP_RouterId,localn.router_id as Local_RouterId,
remoten.igp_router_id Remote_IGP_RouterId, remoten.router_id as Remote_RouterId,
localn.seq, localn.bgp_ls_id as bgpls_id,
CASE WHEN ln.protocol in ('OSPFv2', 'OSPFv3') THEN localn.ospf_area_id ELSE localn.isis_area_id END as AreaId,
ln.mt_id as MT_ID,interface_addr as InterfaceIP,neighbor_addr as NeighborIP,
ln.isIPv4,ln.protocol,igp_metric,local_link_id,remote_link_id,admin_group,max_link_bw,max_resv_bw,
unreserved_bw,te_def_metric,mpls_proto_mask,srlg,ln.name,ln.timestamp,local_node_hash_id,remote_node_hash_id,
localn.igp_router_id as localn_igp_router_id,remoten.igp_router_id as remoten_igp_router_id,
ln.base_attr_hash_id as base_attr_hash_id, ln.peer_hash_id as peer_hash_id,
CASE WHEN ln.iswithdrawn THEN 'WITHDRAWN' ELSE 'ACTIVE' END as state
FROM ls_links ln
JOIN ls_nodes localn ON (ln.local_node_hash_id = localn.hash_id
AND ln.peer_hash_id = localn.peer_hash_id)
JOIN ls_nodes remoten ON (ln.remote_node_hash_id = remoten.hash_id
AND ln.peer_hash_id = remoten.peer_hash_id);

View File

@ -6,7 +6,7 @@
# BUILD: # BUILD:
# tar -c -C ../../ ./obmp-whois ./obmp-docker/whois \ # tar -c -C ../../ ./obmp-whois ./obmp-docker/whois \
# | docker buildx build --platform linux/amd64 --progress=plain \ # | docker buildx build --platform linux/amd64 --progress=plain \
# -f obmp-docker/whois/Dockerfile -t openbmp/whois:2.1.0 - # -f obmp-docker/whois/Dockerfile -t openbmp/whois:2.2.0 -
# ----------------------------------------------- # -----------------------------------------------
# stage: Build # stage: Build