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
{
"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 ,
2026-05-18 20:55:03 -07:00
"links" : [
{
"asDropdown" : true ,
"icon" : "external link" ,
"includeVars" : true ,
"keepTime" : true ,
"tags" : [
"obmp-nav"
] ,
"title" : "OBMP Dashboards" ,
"type" : "dashboards"
}
] ,
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
"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"
} ,
{
"datasource" : {
"type" : "postgres" ,
"uid" : "obmp_postgres"
} ,
"decimals" : 0 ,
"fieldConfig" : {
"defaults" : {
2026-05-18 20:55:03 -07:00
"links" : [ ] ,
"color" : {
"mode" : "palette-classic"
} ,
"custom" : {
"drawStyle" : "line" ,
"lineInterpolation" : "smooth" ,
"lineWidth" : 1 ,
"fillOpacity" : 15 ,
"showPoints" : "never" ,
"spanNulls" : false ,
"axisPlacement" : "auto"
}
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
} ,
"overrides" : [ ]
} ,
"gridPos" : {
"h" : 7 ,
"w" : 11 ,
"x" : 0 ,
"y" : 6
} ,
"id" : 1 ,
"links" : [ ] ,
"options" : {
2026-05-18 20:55:03 -07:00
"legend" : {
"displayMode" : "list" ,
"placement" : "bottom" ,
"showLegend" : true
} ,
"tooltip" : {
"mode" : "multi" ,
"sort" : "none"
}
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
} ,
"pluginVersion" : "9.1.7" ,
"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"
}
]
}
] ,
"title" : "Prefix Advertisements & Withdrawals" ,
2026-05-18 20:55:03 -07:00
"type" : "timeseries"
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
} ,
{
"datasource" : {
"type" : "postgres" ,
"uid" : "obmp_postgres"
} ,
"decimals" : 0 ,
"fieldConfig" : {
"defaults" : {
2026-05-18 20:55:03 -07:00
"links" : [ ] ,
"color" : {
"mode" : "palette-classic"
} ,
"custom" : {
"drawStyle" : "line" ,
"lineInterpolation" : "smooth" ,
"lineWidth" : 1 ,
"fillOpacity" : 15 ,
"showPoints" : "never" ,
"spanNulls" : false ,
"axisPlacement" : "auto"
}
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
} ,
"overrides" : [ ]
} ,
"gridPos" : {
"h" : 7 ,
"w" : 13 ,
"x" : 11 ,
"y" : 6
} ,
"id" : 2 ,
"links" : [ ] ,
"options" : {
2026-05-18 20:55:03 -07:00
"legend" : {
"displayMode" : "list" ,
"placement" : "bottom" ,
"showLegend" : true
} ,
"tooltip" : {
"mode" : "multi" ,
"sort" : "none"
}
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
} ,
"pluginVersion" : "9.1.7" ,
"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"
}
]
}
] ,
"title" : "Changes by Peer" ,
2026-05-18 20:55:03 -07:00
"type" : "timeseries"
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
} ,
{
"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" : [
2026-05-18 20:55:03 -07:00
"obmp-history" ,
"obmp" ,
"obmp-nav"
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
] ,
"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" : ""
}