Add OBMP-Maps dashboard suite: BGP/IGP/AS topology and geo maps

Create a new OBMP-Maps Grafana folder (folderUid 1006) with four
data-visualization dashboards built on nodeGraph and geomap panels:

- BGP Peer Map: routers as nodes, BGP sessions as edges; iBGP/eBGP
  edge typing and operator-editable rr_loopbacks variable to denote
  route reflectors; companion table for sessions to non-monitored
  neighbours.
- IGP / Link-State Topology Map: reworked from LinkState-1004 and
  moved here (uid preserved); scoped by peer feed / protocol / AS so
  the 489-node BGP-LS topology stays readable; SR-capability rings.
- AS Relationship Map: AS adjacency graph from consecutive AS_PATH
  pairs over a 200k-route sample; min-occurrence and focus-AS
  variables; nodes enriched from info_asn whois.
- Geographic Prefix Map: geomap of RIB prefixes and origin ASes by
  IP geolocation, with a note that lab 10.x loopbacks do not
  geolocate; bounded geo_ip join via a sample-size variable.

Also add a data link on the Looking Glass ASN Info panel's origin_as
column that jumps to the ASN View dashboard scoped to that AS.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
sam 2026-05-19 00:07:41 -07:00
parent 26dea47a55
commit f6a100e673
7 changed files with 519 additions and 253 deletions

View File

@ -27,7 +27,17 @@
"id": null, "id": null,
"iteration": 1654877090626, "iteration": 1654877090626,
"links": [ "links": [
{"asDropdown": true,"icon": "external link","includeVars": true,"keepTime": true,"tags": ["obmp-nav"],"title": "OBMP Dashboards","type": "dashboards"} {
"asDropdown": true,
"icon": "external link",
"includeVars": true,
"keepTime": true,
"tags": [
"obmp-nav"
],
"title": "OBMP Dashboards",
"type": "dashboards"
}
], ],
"liveNow": false, "liveNow": false,
"panels": [ "panels": [
@ -58,10 +68,26 @@
"description": "Geolocation of the matched prefix (from the geo_ip table).", "description": "Geolocation of the matched prefix (from the geo_ip table).",
"fieldConfig": { "fieldConfig": {
"defaults": { "defaults": {
"color": {"mode": "thresholds"}, "color": {
"custom": {"hideFrom": {"legend": false,"tooltip": false,"viz": false}}, "mode": "thresholds"
},
"custom": {
"hideFrom": {
"legend": false,
"tooltip": false,
"viz": false
}
},
"mappings": [], "mappings": [],
"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]} "thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
}, },
"overrides": [] "overrides": []
}, },
@ -73,29 +99,72 @@
}, },
"id": 17, "id": 17,
"options": { "options": {
"basemap": {"config": {},"name": "Layer 0","type": "default"}, "basemap": {
"controls": {"mouseWheelZoom": false,"showAttribution": true,"showDebug": false,"showMeasure": false,"showScale": false,"showZoom": true}, "config": {},
"name": "Layer 0",
"type": "default"
},
"controls": {
"mouseWheelZoom": false,
"showAttribution": true,
"showDebug": false,
"showMeasure": false,
"showScale": false,
"showZoom": true
},
"layers": [ "layers": [
{ {
"config": { "config": {
"showLegend": false, "showLegend": false,
"style": { "style": {
"color": {"fixed": "red"}, "color": {
"fixed": "red"
},
"opacity": 0.7, "opacity": 0.7,
"rotation": {"fixed": 0,"max": 360,"min": -360,"mode": "mod"}, "rotation": {
"size": {"fixed": 8,"max": 15,"min": 2}, "fixed": 0,
"symbol": {"fixed": "img/icons/marker/circle.svg","mode": "fixed"}, "max": 360,
"textConfig": {"fontSize": 12,"offsetX": 0,"offsetY": 0,"textAlign": "center","textBaseline": "middle"} "min": -360,
"mode": "mod"
},
"size": {
"fixed": 8,
"max": 15,
"min": 2
},
"symbol": {
"fixed": "img/icons/marker/circle.svg",
"mode": "fixed"
},
"textConfig": {
"fontSize": 12,
"offsetX": 0,
"offsetY": 0,
"textAlign": "center",
"textBaseline": "middle"
}
} }
}, },
"location": {"latitude": "latitude","longitude": "longitude","mode": "coords"}, "location": {
"latitude": "latitude",
"longitude": "longitude",
"mode": "coords"
},
"name": "Prefix Location", "name": "Prefix Location",
"tooltip": true, "tooltip": true,
"type": "markers" "type": "markers"
} }
], ],
"tooltip": {"mode": "details"}, "tooltip": {
"view": {"allLayers": true,"id": "zero","lat": 0,"lon": 0,"zoom": 1} "mode": "details"
},
"view": {
"allLayers": true,
"id": "zero",
"lat": 0,
"lon": 0,
"zoom": 1
}
}, },
"pluginVersion": "9.1.7", "pluginVersion": "9.1.7",
"targets": [ "targets": [
@ -329,12 +398,45 @@
}, },
"fieldConfig": { "fieldConfig": {
"defaults": { "defaults": {
"color": {"mode": "thresholds"}, "color": {
"custom": {"align": "auto","displayMode": "auto","inspect": false}, "mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"mappings": [], "mappings": [],
"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]} "thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
}, },
"overrides": [] "overrides": [
{
"matcher": {
"id": "byName",
"options": "origin_as"
},
"properties": [
{
"id": "links",
"value": [
{
"title": "Open ASN View for AS ${__value.raw}",
"url": "/d/asnview-agg/asn-view?var-asn_num=${__value.raw}",
"targetBlank": true
}
]
}
]
}
]
}, },
"gridPos": { "gridPos": {
"h": 6, "h": 6,
@ -345,7 +447,14 @@
"id": 12, "id": 12,
"links": [], "links": [],
"options": { "options": {
"footer": {"countRows": false,"fields": "","reducer": ["sum"],"show": false}, "footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true "showHeader": true
}, },
"targets": [ "targets": [
@ -391,10 +500,24 @@
}, },
"fieldConfig": { "fieldConfig": {
"defaults": { "defaults": {
"color": {"mode": "thresholds"}, "color": {
"custom": {"align": "auto","displayMode": "auto","inspect": false}, "mode": "thresholds"
},
"custom": {
"align": "auto",
"displayMode": "auto",
"inspect": false
},
"mappings": [], "mappings": [],
"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]} "thresholds": {
"mode": "absolute",
"steps": [
{
"color": "green",
"value": null
}
]
}
}, },
"overrides": [] "overrides": []
}, },
@ -407,7 +530,14 @@
"id": 13, "id": 13,
"links": [], "links": [],
"options": { "options": {
"footer": {"countRows": false,"fields": "","reducer": ["sum"],"show": false}, "footer": {
"countRows": false,
"fields": "",
"reducer": [
"sum"
],
"show": false
},
"showHeader": true "showHeader": true
}, },
"targets": [ "targets": [

View File

@ -1,230 +0,0 @@
{
"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": [
{
"asDropdown": true,
"icon": "external link",
"includeVars": true,
"keepTime": true,
"tags": [
"obmp-nav"
],
"title": "OBMP Dashboards",
"type": "dashboards"
}
],
"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 <= 16777215\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 <= 16777215\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-nav",
"linkstate",
"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 <= 16777215\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 <= 16777215\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": ""
}

View File

@ -0,0 +1,70 @@
{
"annotations": {"list": [{"builtIn": 1,"datasource": {"type": "datasource","uid": "grafana"},"enable": true,"hide": true,"iconColor": "rgba(0, 211, 255, 1)","name": "Annotations & Alerts","type": "dashboard"}]},
"description": "AS adjacency graph derived from consecutive AS pairs in observed AS_PATHs. Edge label = how many times that adjacency appears in a 200k-route sample. Raise 'Min occurrences' to thin the graph; set 'Focus AS' to a 1-hop view around one AS. Manual refresh — the query explodes ~200k AS_PATH arrays.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [{"asDropdown": true,"icon": "external link","includeVars": true,"keepTime": true,"tags": ["obmp-nav"],"title": "OBMP Dashboards","type": "dashboards"}],
"liveNow": false,
"panels": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Each node is an AS (enriched from info_asn whois data); each edge is an adjacency seen in the AS_PATH sample. Edge label is the occurrence count.",
"fieldConfig": {"defaults": {},"overrides": []},
"gridPos": {"h": 24,"w": 24,"x": 0,"y": 0},
"id": 1,
"options": {"nodes": {"mainStatUnit": "","secondaryStatUnit": ""}},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH sample AS (SELECT as_path FROM base_attrs WHERE as_path_count >= 2 LIMIT 200000),\npairs AS (\n SELECT a.asn AS src, b.asn AS dst\n FROM sample ba\n CROSS JOIN LATERAL unnest(ba.as_path) WITH ORDINALITY a(asn,ord)\n JOIN LATERAL unnest(ba.as_path) WITH ORDINALITY b(asn,ord) ON b.ord = a.ord + 1\n WHERE a.asn <> b.asn\n),\nedges AS (\n SELECT LEAST(src,dst) AS x, GREATEST(src,dst) AS y, COUNT(*) AS occ\n FROM pairs GROUP BY 1,2 HAVING COUNT(*) >= $min_occ ORDER BY occ DESC LIMIT 300\n),\nfedges AS (\n SELECT * FROM edges\n WHERE '$focus_as' = '' OR x::text = '$focus_as' OR y::text = '$focus_as'\n),\nnlist AS (SELECT x AS asn FROM fedges UNION SELECT y FROM fedges),\ndeg AS (SELECT asn, COUNT(*) AS d FROM (SELECT x AS asn FROM fedges UNION ALL SELECT y FROM fedges) z GROUP BY asn)\nSELECT n.asn::text AS id,\n COALESCE(NULLIF(ia.as_name,''),'AS'||n.asn) AS title,\n 'AS ' || n.asn AS mainstat,\n COALESCE(NULLIF(ia.country,''),'?') || ' · ' || dg.d::text || ' links' AS secondarystat,\n COALESCE(NULLIF(ia.org_name,''),'—') AS detail__org,\n COALESCE(NULLIF(ia.country,''),'—') AS detail__country,\n dg.d::text AS detail__degree\nFROM nlist n\nLEFT JOIN info_asn ia ON ia.asn = n.asn\nLEFT JOIN deg dg ON dg.asn = n.asn\nORDER BY dg.d DESC",
"refId": "nodes"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH sample AS (SELECT as_path FROM base_attrs WHERE as_path_count >= 2 LIMIT 200000),\npairs AS (\n SELECT a.asn AS src, b.asn AS dst\n FROM sample ba\n CROSS JOIN LATERAL unnest(ba.as_path) WITH ORDINALITY a(asn,ord)\n JOIN LATERAL unnest(ba.as_path) WITH ORDINALITY b(asn,ord) ON b.ord = a.ord + 1\n WHERE a.asn <> b.asn\n),\nedges AS (\n SELECT LEAST(src,dst) AS x, GREATEST(src,dst) AS y, COUNT(*) AS occ\n FROM pairs GROUP BY 1,2 HAVING COUNT(*) >= $min_occ ORDER BY occ DESC LIMIT 300\n)\nSELECT x::text || '-' || y::text AS id,\n x::text AS source, y::text AS target,\n occ AS mainstat,\n occ::text || ' paths' AS detail__occurrences\nFROM edges\nWHERE '$focus_as' = '' OR x::text = '$focus_as' OR y::text = '$focus_as'",
"refId": "edges"
}
],
"title": "AS Adjacency Graph",
"type": "nodeGraph"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "The strongest AS adjacencies in the sample, with whois names for both endpoints.",
"fieldConfig": {"defaults": {"custom": {"align": "auto","displayMode": "auto"}},"overrides": [{"matcher": {"id": "byName","options": "Occurrences"},"properties": [{"id": "custom.displayMode","value": "gradient-gauge"},{"id": "color","value": {"mode": "continuous-BlPu"}}]}]},
"gridPos": {"h": 10,"w": 24,"x": 0,"y": 24},
"id": 2,
"options": {"showHeader": true,"sortBy": [{"desc": true,"displayName": "Occurrences"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH sample AS (SELECT as_path FROM base_attrs WHERE as_path_count >= 2 LIMIT 200000),\npairs AS (\n SELECT a.asn AS src, b.asn AS dst\n FROM sample ba\n CROSS JOIN LATERAL unnest(ba.as_path) WITH ORDINALITY a(asn,ord)\n JOIN LATERAL unnest(ba.as_path) WITH ORDINALITY b(asn,ord) ON b.ord = a.ord + 1\n WHERE a.asn <> b.asn\n),\nedges AS (\n SELECT LEAST(src,dst) AS x, GREATEST(src,dst) AS y, COUNT(*) AS occ\n FROM pairs GROUP BY 1,2 HAVING COUNT(*) >= $min_occ ORDER BY occ DESC LIMIT 300\n)\nSELECT e.x AS \"AS A\",\n COALESCE(NULLIF(ax.as_name,''),'—') AS \"Name A\",\n e.y AS \"AS B\",\n COALESCE(NULLIF(ay.as_name,''),'—') AS \"Name B\",\n e.occ AS \"Occurrences\"\nFROM edges e\nLEFT JOIN info_asn ax ON ax.asn = e.x\nLEFT JOIN info_asn ay ON ay.asn = e.y\nWHERE '$focus_as' = '' OR e.x::text = '$focus_as' OR e.y::text = '$focus_as'\nORDER BY e.occ DESC",
"refId": "A"
}
],
"title": "Top AS Adjacencies",
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": ["obmp", "obmp-nav", "obmp-maps", "asn"],
"templating": {
"list": [
{"name": "min_occ","type": "custom","label": "Min occurrences","description": "Only draw adjacencies seen at least this many times in the 200k-route sample. Raise it to thin a cluttered graph.","query": "5,20,100,500","current": {"text": "20","value": "20"},"options": [{"text": "5","value": "5","selected": false},{"text": "20","value": "20","selected": true},{"text": "100","value": "100","selected": false},{"text": "500","value": "500","selected": false}],"hide": 0},
{"name": "focus_as","type": "textbox","label": "Focus AS","description": "Optional. Enter an ASN to show only adjacencies touching that AS (1-hop view). Leave blank for the full graph.","query": "","current": {"text": "","value": ""},"options": [{"text": "","value": "","selected": true}],"hide": 0}
]
},
"time": {"from": "now-6h","to": "now"},
"timepicker": {},
"timezone": "",
"title": "AS Relationship Map",
"uid": "as-rel-map",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,78 @@
{
"annotations": {"list": [{"builtIn": 1,"datasource": {"type": "datasource","uid": "grafana"},"enable": true,"hide": true,"iconColor": "rgba(0, 211, 255, 1)","name": "Annotations & Alerts","type": "dashboard"}]},
"description": "BGP peering topology — every monitored router as a node, every BGP session as an edge. Route reflectors (set the RR Loopbacks variable) show a red ring. Sessions to non-monitored neighbours (e.g. the ExaBGP injector) are listed in the table below.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [{"asDropdown": true,"icon": "external link","includeVars": true,"keepTime": true,"tags": ["obmp-nav"],"title": "OBMP Dashboards","type": "dashboards"}],
"liveNow": false,
"panels": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Routers and their BGP sessions. Node ring: red = Route Reflector (per the RR Loopbacks variable), blue = standard router. Edge label shows iBGP vs eBGP.",
"fieldConfig": {
"defaults": {},
"overrides": [
{"matcher": {"id": "byName","options": "arc__rr"},"properties": [{"id": "color","value": {"fixedColor": "red","mode": "fixed"}},{"id": "displayName","value": "Route Reflector"}]},
{"matcher": {"id": "byName","options": "arc__std"},"properties": [{"id": "color","value": {"fixedColor": "blue","mode": "fixed"}},{"id": "displayName","value": "Router"}]}
]
},
"gridPos": {"h": 22,"w": 24,"x": 0,"y": 0},
"id": 1,
"options": {"nodes": {"mainStatUnit": "","secondaryStatUnit": ""}},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT vp.router_hash_id::text AS id,\n MAX(vp.routername) AS title,\n 'AS ' || MAX(vp.localasn)::text AS mainstat,\n COUNT(DISTINCT vp.peerbgpid) FILTER (WHERE vp.peer_state = 'up')::text || ' peers up' AS secondarystat,\n host(MAX(vp.localbgpid)) AS detail__bgp_id,\n CASE WHEN host(MAX(vp.localbgpid)) IN (${rr_loopbacks:singlequote}) THEN 'Route Reflector' ELSE 'Router' END AS detail__role,\n CASE WHEN host(MAX(vp.localbgpid)) IN (${rr_loopbacks:singlequote}) THEN 1 ELSE 0 END AS arc__rr,\n CASE WHEN host(MAX(vp.localbgpid)) IN (${rr_loopbacks:singlequote}) THEN 0 ELSE 1 END AS arc__std\nFROM v_peers vp\nWHERE vp.localbgpid IS NOT NULL\nGROUP BY vp.router_hash_id\nORDER BY title",
"refId": "nodes"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH rid AS (\n SELECT DISTINCT router_hash_id, host(localbgpid) AS bgpid\n FROM v_peers WHERE localbgpid IS NOT NULL\n),\nsess AS (\n SELECT DISTINCT\n LEAST(vp.router_hash_id, rid2.router_hash_id)::text AS a,\n GREATEST(vp.router_hash_id, rid2.router_hash_id)::text AS b,\n CASE WHEN vp.peerasn = vp.localasn THEN 'iBGP' ELSE 'eBGP' END AS kind\n FROM v_peers vp\n JOIN rid rid2 ON rid2.bgpid = host(vp.peerbgpid)\n WHERE vp.peer_state = 'up' AND vp.router_hash_id <> rid2.router_hash_id\n)\nSELECT a || '-' || b AS id, a AS source, b AS target,\n MAX(kind) AS mainstat, MAX(kind) AS detail__session_type\nFROM sess GROUP BY a, b",
"refId": "edges"
}
],
"title": "BGP Peering Topology",
"type": "nodeGraph"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Up BGP sessions whose far end is not a monitored router (no node to draw an edge to) — e.g. eBGP sessions to the ExaBGP route injector or external peers.",
"fieldConfig": {
"defaults": {"custom": {"align": "auto","displayMode": "auto"}},
"overrides": [{"matcher": {"id": "byName","options": "Type"},"properties": [{"id": "custom.displayMode","value": "color-background"},{"id": "mappings","value": [{"options": {"eBGP": {"color": "orange","index": 0},"iBGP": {"color": "blue","index": 1}},"type": "value"}]}]}]
},
"gridPos": {"h": 8,"w": 24,"x": 0,"y": 22},
"id": 2,
"options": {"showHeader": true,"sortBy": [{"desc": false,"displayName": "Router"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT vp.routername AS \"Router\",\n host(vp.peerip) AS \"Peer\",\n vp.peerasn AS \"Peer AS\",\n CASE WHEN vp.peerasn = vp.localasn THEN 'iBGP' ELSE 'eBGP' END AS \"Type\",\n vp.peer_state AS \"State\"\nFROM v_peers vp\nWHERE vp.peer_state = 'up'\n AND NOT EXISTS (SELECT 1 FROM v_peers r WHERE host(r.localbgpid) = host(vp.peerbgpid))\nORDER BY vp.routername, vp.peerip",
"refId": "A"
}
],
"title": "Sessions to External / Non-Monitored Neighbours",
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": ["obmp", "obmp-nav", "obmp-maps", "bgp"],
"templating": {
"list": [
{"name": "rr_loopbacks","type": "custom","label": "RR Loopbacks","description": "Loopback / BGP router-id addresses of your route reflectors. Edit this list for your environment.","query": "10.10.255.0,10.10.255.20,10.11.255.0,10.11.255.20","multi": true,"includeAll": true,"current": {"text": ["All"],"value": ["$__all"]},"options": [{"text": "All","value": "$__all","selected": true},{"text": "10.10.255.0","value": "10.10.255.0","selected": false},{"text": "10.10.255.20","value": "10.10.255.20","selected": false},{"text": "10.11.255.0","value": "10.11.255.0","selected": false},{"text": "10.11.255.20","value": "10.11.255.20","selected": false}],"hide": 0}
]
},
"time": {"from": "now-6h","to": "now"},
"timepicker": {},
"timezone": "",
"title": "BGP Peer Map",
"uid": "bgp-peer-map",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,104 @@
{
"annotations": {"list": [{"builtIn": 1,"datasource": {"type": "datasource","uid": "grafana"},"enable": true,"hide": true,"iconColor": "rgba(0, 211, 255, 1)","name": "Annotations & Alerts","type": "dashboard"}]},
"description": "Geographic view of the routes in the RIB — prefixes and origin ASes plotted by IP-geolocation (geo_ip). Shows the injected real-IP prefixes, not the lab fabric (lab routers use 10.x loopbacks that do not geolocate — see the BGP/IGP node-graph maps for the fabric). Manual refresh — the geo_ip containment join is heavy.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [{"asDropdown": true,"icon": "external link","includeVars": true,"keepTime": true,"tags": ["obmp-nav"],"title": "OBMP Dashboards","type": "dashboards"}],
"liveNow": false,
"panels": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"gridPos": {"h": 4,"w": 24,"x": 0,"y": 0},
"id": 10,
"options": {"code": {"language": "plaintext","showLineNumbers": false,"showMiniMap": false},"content": "## Geographic Prefix & Origin-AS Map\n\nThese maps plot RIB routes by **IP geolocation** (`geo_ip`). The CML lab routers use `10.x` loopback addressing, which does **not** geolocate — so this dashboard visualises the **injected real-IP prefixes** and their **origin ASes**, not the lab fabric itself. For the lab topology see **BGP Peer Map** and **IGP / Link-State Topology Map**.\n\nQueries are bounded by the **Sample size** variable and run on **manual refresh** only — the `geo_ip` containment join scans ~20M rows.","mode": "markdown"},
"pluginVersion": "9.1.7",
"title": "",
"type": "text"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Each marker is a RIB prefix placed at the location of a covering geo_ip block. Bounded by the Sample size variable.",
"fieldConfig": {"defaults": {"color": {"mode": "continuous-GrYlRd"},"custom": {"hideFrom": {"legend": false,"tooltip": false,"viz": false}}},"overrides": []},
"gridPos": {"h": 16,"w": 12,"x": 0,"y": 4},
"id": 1,
"options": {
"basemap": {"config": {},"name": "Layer 0","type": "default"},
"controls": {"mouseWheelZoom": true,"showAttribution": true,"showDebug": false,"showMeasure": false,"showScale": false,"showZoom": true},
"layers": [{"config": {"showLegend": false,"style": {"color": {"fixed": "blue"},"opacity": 0.4,"size": {"fixed": 4},"symbol": {"fixed": "img/icons/marker/circle.svg","mode": "fixed"}}},"location": {"latitude": "latitude","longitude": "longitude","mode": "coords"},"name": "Prefixes","tooltip": true,"type": "markers"}],
"tooltip": {"mode": "details"},
"view": {"id": "zero","lat": 25,"lon": 0,"zoom": 2}
},
"pluginVersion": "9.1.7",
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH p AS (\n SELECT DISTINCT prefix FROM ip_rib\n WHERE iswithdrawn = false AND isipv4 = true AND family(prefix) = 4\n AND NOT (prefix << '10.0.0.0/8')\n LIMIT $sample_limit\n)\nSELECT host(p.prefix) AS prefix, g.latitude, g.longitude\nFROM p CROSS JOIN LATERAL (\n SELECT latitude, longitude FROM geo_ip WHERE ip >>= p.prefix LIMIT 1\n) g",
"refId": "A"
}
],
"title": "RIB Prefix Geolocation",
"type": "geomap"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "One marker per origin AS, placed at the geolocation of one of its prefixes and enriched with whois name.",
"fieldConfig": {"defaults": {"color": {"mode": "continuous-BlPu"},"custom": {"hideFrom": {"legend": false,"tooltip": false,"viz": false}}},"overrides": []},
"gridPos": {"h": 16,"w": 12,"x": 12,"y": 4},
"id": 2,
"options": {
"basemap": {"config": {},"name": "Layer 0","type": "default"},
"controls": {"mouseWheelZoom": true,"showAttribution": true,"showDebug": false,"showMeasure": false,"showScale": false,"showZoom": true},
"layers": [{"config": {"showLegend": false,"style": {"color": {"fixed": "orange"},"opacity": 0.6,"size": {"fixed": 6},"symbol": {"fixed": "img/icons/marker/circle.svg","mode": "fixed"}}},"location": {"latitude": "latitude","longitude": "longitude","mode": "coords"},"name": "Origin ASes","tooltip": true,"type": "markers"}],
"tooltip": {"mode": "details"},
"view": {"id": "zero","lat": 25,"lon": 0,"zoom": 2}
},
"pluginVersion": "9.1.7",
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH oa AS (\n SELECT origin_as, MIN(prefix) AS prefix\n FROM ip_rib\n WHERE iswithdrawn = false AND isipv4 = true AND family(prefix) = 4\n AND NOT (prefix << '10.0.0.0/8') AND origin_as IS NOT NULL\n GROUP BY origin_as\n)\nSELECT 'AS' || oa.origin_as AS \"AS\",\n COALESCE(NULLIF(ia.as_name,''),'AS'||oa.origin_as) AS as_name,\n g.latitude, g.longitude\nFROM oa\nLEFT JOIN info_asn ia ON ia.asn = oa.origin_as\nCROSS JOIN LATERAL (\n SELECT latitude, longitude FROM geo_ip WHERE ip >>= oa.prefix LIMIT 1\n) g",
"refId": "A"
}
],
"title": "Origin-AS Geographic Distribution",
"type": "geomap"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Origin ASes grouped by whois country. ASes with no whois enrichment show as (unknown). Fast — no geo_ip join.",
"fieldConfig": {"defaults": {"custom": {"align": "auto","displayMode": "auto"}},"overrides": [{"matcher": {"id": "byName","options": "Origin ASes"},"properties": [{"id": "custom.displayMode","value": "gradient-gauge"},{"id": "color","value": {"mode": "continuous-BlPu"}}]}]},
"gridPos": {"h": 10,"w": 24,"x": 0,"y": 20},
"id": 3,
"options": {"showHeader": true,"sortBy": [{"desc": true,"displayName": "Origin ASes"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT COALESCE(NULLIF(ia.country,''),'(unknown)') AS \"Country\",\n COUNT(DISTINCT r.origin_as) AS \"Origin ASes\"\nFROM (SELECT DISTINCT origin_as FROM ip_rib WHERE iswithdrawn = false AND origin_as IS NOT NULL) r\nLEFT JOIN info_asn ia ON ia.asn = r.origin_as\nGROUP BY 1\nORDER BY 2 DESC",
"refId": "A"
}
],
"title": "Origin-AS by Country",
"type": "table"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": ["obmp", "obmp-nav", "obmp-maps", "geo"],
"templating": {
"list": [
{"name": "sample_limit","type": "custom","label": "Sample size","description": "Number of distinct prefixes to geolocate on the RIB Prefix map. Larger = slower (the geo_ip join scans ~20M rows).","query": "2000,5000,10000","current": {"text": "2000","value": "2000"},"options": [{"text": "2000","value": "2000","selected": true},{"text": "5000","value": "5000","selected": false},{"text": "10000","value": "10000","selected": false}],"hide": 0}
]
},
"time": {"from": "now-6h","to": "now"},
"timepicker": {},
"timezone": "",
"title": "Geographic Prefix Map",
"uid": "geo-prefix-map",
"version": 1,
"weekStart": ""
}

View File

@ -0,0 +1,103 @@
{
"annotations": {"list": [{"builtIn": 1,"datasource": {"type": "datasource","uid": "grafana"},"enable": true,"hide": true,"iconColor": "rgba(0, 211, 255, 1)","name": "Annotations & Alerts","type": "dashboard"}]},
"description": "IGP link-state topology (BGP-LS) as a node graph. Scope with the BGP Peer feed, IGP protocol, and AS to keep the graph readable. Edge labels are IGP metric; node rings show Segment Routing capability.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 0,
"id": null,
"links": [{"asDropdown": true,"icon": "external link","includeVars": true,"keepTime": true,"tags": ["obmp-nav"],"title": "OBMP Dashboards","type": "dashboards"}],
"liveNow": false,
"panels": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Every BGP-LS node and link for the selected peer feed / protocol / AS. SR-capable nodes show a green ring.",
"fieldConfig": {
"defaults": {},
"overrides": [
{"matcher": {"id": "byName","options": "arc__sr"},"properties": [{"id": "color","value": {"fixedColor": "green","mode": "fixed"}},{"id": "displayName","value": "SR-capable"}]},
{"matcher": {"id": "byName","options": "arc__plain"},"properties": [{"id": "color","value": {"fixedColor": "blue","mode": "fixed"}},{"id": "displayName","value": "No SR"}]}
]
},
"gridPos": {"h": 28,"w": 24,"x": 0,"y": 0},
"id": 2,
"options": {"nodes": {"mainStatUnit": "","secondaryStatUnit": ""}},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT n.hash_id::text AS id,\n CASE WHEN COALESCE(n.name,'') = '' THEN n.igp_router_id ELSE n.name END AS title,\n n.router_id AS mainstat,\n n.protocol::text AS secondarystat,\n n.igp_router_id AS detail__igp_id,\n 'AS ' || n.asn AS detail__asn,\n COALESCE(NULLIF(n.sr_capabilities,''),'none') AS detail__sr_caps,\n CASE WHEN COALESCE(n.sr_capabilities,'') <> '' THEN 1 ELSE 0 END AS arc__sr,\n CASE WHEN COALESCE(n.sr_capabilities,'') = '' THEN 1 ELSE 0 END AS arc__plain\nFROM ls_nodes n\nWHERE n.iswithdrawn = false\n AND n.peer_hash_id = '$peer_hash'\n AND ('$protocol' = '' OR n.protocol::text = '$protocol')\n AND ($asn = 0 OR n.asn = $asn)\nORDER BY title",
"refId": "nodes"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT l.local_node_hash_id::text || '->' || l.remote_node_hash_id::text AS id,\n l.local_node_hash_id::text AS source,\n l.remote_node_hash_id::text AS target,\n MAX(l.igp_metric)::bigint AS mainstat,\n MAX(l.protocol::text) AS secondarystat,\n MAX(l.te_def_metric)::text AS detail__te_metric,\n MAX(l.max_link_bw)::text AS detail__max_bw\nFROM ls_links l\nJOIN ls_nodes ln ON ln.hash_id = l.local_node_hash_id AND ln.peer_hash_id = l.peer_hash_id\nWHERE l.iswithdrawn = false\n AND l.peer_hash_id = '$peer_hash'\n AND ('$protocol' = '' OR l.protocol::text = '$protocol')\n AND ($asn = 0 OR ln.asn = $asn)\nGROUP BY l.local_node_hash_id, l.remote_node_hash_id",
"refId": "edges"
}
],
"title": "IGP / Link-State Topology",
"type": "nodeGraph"
}
],
"schemaVersion": 36,
"style": "dark",
"tags": ["obmp", "obmp-nav", "obmp-maps", "linkstate"],
"templating": {
"list": [
{
"current": {},
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"definition": "SELECT __text,__value FROM (select peername as __text, peer_hash_id as __value, count(*) as count from v_ls_nodes group by peername,peer_hash_id) d where count > 0",
"hide": 0,
"includeAll": false,
"label": "BGP Peer feed",
"multi": false,
"name": "peer_hash",
"options": [],
"query": "SELECT __text,__value FROM (select peername as __text, peer_hash_id as __value, count(*) as count from v_ls_nodes group by peername,peer_hash_id) d where count > 0",
"refresh": 1,
"sort": 1,
"type": "query"
},
{
"allValue": "",
"current": {"selected": true,"text": "All","value": "$__all"},
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"definition": "SELECT DISTINCT protocol::text FROM ls_nodes WHERE iswithdrawn=false AND protocol IS NOT NULL AND protocol::text <> '' ORDER BY 1",
"hide": 0,
"includeAll": true,
"label": "IGP Protocol",
"multi": false,
"name": "protocol",
"options": [],
"query": "SELECT DISTINCT protocol::text FROM ls_nodes WHERE iswithdrawn=false AND protocol IS NOT NULL AND protocol::text <> '' ORDER BY 1",
"refresh": 1,
"sort": 1,
"type": "query"
},
{
"allValue": "0",
"current": {"selected": true,"text": "All","value": "$__all"},
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"definition": "SELECT DISTINCT asn FROM ls_nodes WHERE iswithdrawn=false AND asn > 0 ORDER BY asn",
"hide": 0,
"includeAll": true,
"label": "AS",
"multi": false,
"name": "asn",
"options": [],
"query": "SELECT DISTINCT asn FROM ls_nodes WHERE iswithdrawn=false AND asn > 0 ORDER BY asn",
"refresh": 1,
"sort": 3,
"type": "query"
}
]
},
"time": {"from": "now-6h","to": "now"},
"timepicker": {},
"timezone": "",
"title": "IGP / Link-State Topology Map",
"uid": "SNOLrQlnz",
"version": 1,
"weekStart": ""
}

View File

@ -125,3 +125,14 @@ providers:
options: options:
path: /var/lib/grafana/dashboards/Telemetry-3001 path: /var/lib/grafana/dashboards/Telemetry-3001
foldersFromFilesStructure: false foldersFromFilesStructure: false
- name: 'OpenBMP-Maps'
orgId: 1
folder: 'OBMP-Maps'
folderUid: '1006'
type: file
disableDeletion: false
updateIntervalSeconds: 30
allowUiUpdates: true
options:
path: /var/lib/grafana/dashboards/obmp/Maps-1006
foldersFromFilesStructure: false