419 lines
29 KiB
JSON
Raw Permalink Normal View History

{
"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": "Compare Route Reflector Loc-RIB tables via BMP Adj-RIB-In. Surfaces missing prefixes, attribute differences, and per-client consistency between two RRs.",
"editable": true,
"fiscalYearStartMonth": 0,
"graphTooltip": 1,
"id": null,
"links": [],
"panels": [
{
"datasource": {"type": "datasource","uid": "grafana"},
"gridPos": {"h": 5,"w": 24,"x": 0,"y": 0},
"id": 1,
"options": {
"content": "## Route Reflector Loc-RIB Diff\n\nCompares the RIB tables of two Route Reflectors via BMP. Select your two RRs using the **RR1** and **RR2** dropdowns above.\n\n**What this shows:**\n- **Summary stats** — prefix counts and diff totals\n- **Missing prefixes** — routes present on one RR but not the other (expected for each RR's own locally-originated routes)\n- **Attribute differences** — same prefix on both RRs but with different next-hop, AS path, or other attributes (indicates different best-path selection)\n- **Per-client consistency** — route counts each client sends to each RR (should be identical in a healthy RR cluster)",
"mode": "markdown"
},
"title": "RR Loc-RIB Diff — Overview",
"type": "text"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Total active (non-withdrawn) prefixes on RR1",
"fieldConfig": {
"defaults": {"color": {"fixedColor": "blue","mode": "fixed"},"thresholds": {"mode": "absolute","steps": [{"color": "blue","value": null}]}},
"overrides": []
},
"gridPos": {"h": 4,"w": 4,"x": 0,"y": 5},
"id": 10,
"options": {"colorMode": "value","graphMode": "none","justifyMode": "auto","orientation": "auto","reduceOptions": {"calcs": ["lastNotNull"],"fields": "","values": false},"textMode": "auto"},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT COUNT(DISTINCT r.prefix || '/' || r.prefix_len) AS \"prefixes\"\nFROM ip_rib r\nJOIN bgp_peers p ON p.hash_id = r.peer_hash_id\nJOIN routers rt ON rt.hash_id = p.router_hash_id\nWHERE rt.name = '$rr1' AND r.iswithdrawn = false",
"refId": "A"
}
],
"title": "$rr1 Prefixes",
"type": "stat"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Total active (non-withdrawn) prefixes on RR2",
"fieldConfig": {
"defaults": {"color": {"fixedColor": "green","mode": "fixed"},"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]}},
"overrides": []
},
"gridPos": {"h": 4,"w": 4,"x": 4,"y": 5},
"id": 11,
"options": {"colorMode": "value","graphMode": "none","justifyMode": "auto","orientation": "auto","reduceOptions": {"calcs": ["lastNotNull"],"fields": "","values": false},"textMode": "auto"},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT COUNT(DISTINCT r.prefix || '/' || r.prefix_len) AS \"prefixes\"\nFROM ip_rib r\nJOIN bgp_peers p ON p.hash_id = r.peer_hash_id\nJOIN routers rt ON rt.hash_id = p.router_hash_id\nWHERE rt.name = '$rr2' AND r.iswithdrawn = false",
"refId": "A"
}
],
"title": "$rr2 Prefixes",
"type": "stat"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Prefixes present on RR1 but missing from RR2",
"fieldConfig": {
"defaults": {"color": {"fixedColor": "orange","mode": "fixed"},"thresholds": {"mode": "absolute","steps": [{"color": "orange","value": null},{"color": "red","value": 1}]}},
"overrides": []
},
"gridPos": {"h": 4,"w": 4,"x": 8,"y": 5},
"id": 12,
"options": {"colorMode": "value","graphMode": "none","justifyMode": "auto","orientation": "auto","reduceOptions": {"calcs": ["lastNotNull"],"fields": "","values": false},"textMode": "auto"},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT COUNT(*) AS \"only_rr1\"\nFROM (\n SELECT DISTINCT r1.prefix, r1.prefix_len\n FROM ip_rib r1\n JOIN bgp_peers p1 ON p1.hash_id = r1.peer_hash_id\n JOIN routers rt1 ON rt1.hash_id = p1.router_hash_id\n WHERE rt1.name = '$rr1' AND r1.iswithdrawn = false\n EXCEPT\n SELECT DISTINCT r2.prefix, r2.prefix_len\n FROM ip_rib r2\n JOIN bgp_peers p2 ON p2.hash_id = r2.peer_hash_id\n JOIN routers rt2 ON rt2.hash_id = p2.router_hash_id\n WHERE rt2.name = '$rr2' AND r2.iswithdrawn = false\n) sub",
"refId": "A"
}
],
"title": "Only on $rr1",
"type": "stat"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Prefixes present on RR2 but missing from RR1",
"fieldConfig": {
"defaults": {"color": {"fixedColor": "orange","mode": "fixed"},"thresholds": {"mode": "absolute","steps": [{"color": "orange","value": null},{"color": "red","value": 1}]}},
"overrides": []
},
"gridPos": {"h": 4,"w": 4,"x": 12,"y": 5},
"id": 13,
"options": {"colorMode": "value","graphMode": "none","justifyMode": "auto","orientation": "auto","reduceOptions": {"calcs": ["lastNotNull"],"fields": "","values": false},"textMode": "auto"},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT COUNT(*) AS \"only_rr2\"\nFROM (\n SELECT DISTINCT r2.prefix, r2.prefix_len\n FROM ip_rib r2\n JOIN bgp_peers p2 ON p2.hash_id = r2.peer_hash_id\n JOIN routers rt2 ON rt2.hash_id = p2.router_hash_id\n WHERE rt2.name = '$rr2' AND r2.iswithdrawn = false\n EXCEPT\n SELECT DISTINCT r1.prefix, r1.prefix_len\n FROM ip_rib r1\n JOIN bgp_peers p1 ON p1.hash_id = r1.peer_hash_id\n JOIN routers rt1 ON rt1.hash_id = p1.router_hash_id\n WHERE rt1.name = '$rr1' AND r1.iswithdrawn = false\n) sub",
"refId": "A"
}
],
"title": "Only on $rr2",
"type": "stat"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Prefixes present on both RRs but with different best-path attributes (next-hop, AS path, MED, or local-pref)",
"fieldConfig": {
"defaults": {"color": {"fixedColor": "yellow","mode": "fixed"},"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 10}]}},
"overrides": []
},
"gridPos": {"h": 4,"w": 4,"x": 16,"y": 5},
"id": 14,
"options": {"colorMode": "value","graphMode": "none","justifyMode": "auto","orientation": "auto","reduceOptions": {"calcs": ["lastNotNull"],"fields": "","values": false},"textMode": "auto"},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH c1 AS (\n SELECT DISTINCT ON (r.prefix, r.prefix_len)\n r.prefix, r.prefix_len, ba.next_hop, ba.as_path,\n COALESCE(ba.local_pref, 0) AS lp, COALESCE(ba.med, 0) AS med\n FROM ip_rib r\n JOIN bgp_peers p ON p.hash_id = r.peer_hash_id\n JOIN routers rt ON rt.hash_id = p.router_hash_id\n JOIN base_attrs ba ON ba.hash_id = r.base_attr_hash_id\n WHERE rt.name = '$rr1' AND r.iswithdrawn = false\n ORDER BY r.prefix, r.prefix_len, ba.local_pref DESC NULLS LAST\n),\nc2 AS (\n SELECT DISTINCT ON (r.prefix, r.prefix_len)\n r.prefix, r.prefix_len, ba.next_hop, ba.as_path,\n COALESCE(ba.local_pref, 0) AS lp, COALESCE(ba.med, 0) AS med\n FROM ip_rib r\n JOIN bgp_peers p ON p.hash_id = r.peer_hash_id\n JOIN routers rt ON rt.hash_id = p.router_hash_id\n JOIN base_attrs ba ON ba.hash_id = r.base_attr_hash_id\n WHERE rt.name = '$rr2' AND r.iswithdrawn = false\n ORDER BY r.prefix, r.prefix_len, ba.local_pref DESC NULLS LAST\n)\nSELECT COUNT(*) AS \"attr_diffs\"\nFROM c1 JOIN c2 ON c1.prefix = c2.prefix AND c1.prefix_len = c2.prefix_len\nWHERE c1.next_hop != c2.next_hop OR c1.as_path != c2.as_path\n OR c1.lp != c2.lp OR c1.med != c2.med",
"refId": "A"
}
],
"title": "Attribute Diffs",
"type": "stat"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Total RIB entries (including multiple paths per prefix) on each RR",
"fieldConfig": {
"defaults": {"color": {"fixedColor": "purple","mode": "fixed"},"thresholds": {"mode": "absolute","steps": [{"color": "purple","value": null}]}},
"overrides": []
},
"gridPos": {"h": 4,"w": 4,"x": 20,"y": 5},
"id": 15,
"options": {"colorMode": "value","graphMode": "none","justifyMode": "auto","orientation": "auto","reduceOptions": {"calcs": ["lastNotNull"],"fields": "","values": false},"textMode": "auto"},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT\n SUM(CASE WHEN rt.name = '$rr1' THEN 1 ELSE 0 END) AS \"RR1 Total Paths\",\n SUM(CASE WHEN rt.name = '$rr2' THEN 1 ELSE 0 END) AS \"RR2 Total Paths\"\nFROM ip_rib r\nJOIN bgp_peers p ON p.hash_id = r.peer_hash_id\nJOIN routers rt ON rt.hash_id = p.router_hash_id\nWHERE rt.name IN ('$rr1', '$rr2') AND r.iswithdrawn = false",
"refId": "A"
}
],
"title": "Total RIB Paths",
"type": "stat"
},
{
"collapsed": false,
"gridPos": {"h": 1,"w": 24,"x": 0,"y": 9},
"id": 20,
"title": "Missing Prefixes",
"type": "row"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Prefixes present on RR1 but NOT on RR2. Expected: RR2's own locally-originated routes won't appear here (they're on RR2 only). Unexpected entries indicate a convergence or session issue.",
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"},"custom": {"align": "auto","displayMode": "auto","filterable": true},"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]}},
"overrides": [
{"matcher": {"id": "byName","options": "Learned From"},"properties": [{"id": "custom.width","value": 130}]},
{"matcher": {"id": "byName","options": "Origin AS"},"properties": [{"id": "custom.width","value": 90}]},
{"matcher": {"id": "byName","options": "Next Hop"},"properties": [{"id": "custom.width","value": 140}]}
]
},
"gridPos": {"h": 10,"w": 12,"x": 0,"y": 10},
"id": 21,
"options": {"footer": {"fields": "","reducer": ["count"],"show": true},"showHeader": true,"sortBy": [{"desc": false,"displayName": "Prefix"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT DISTINCT ON (r1.prefix, r1.prefix_len)\n r1.prefix::text AS \"Prefix\",\n ba.origin_as AS \"Origin AS\",\n ba.next_hop::text AS \"Next Hop\",\n ba.as_path::text AS \"AS Path\",\n p1.peer_addr::text AS \"Learned From\"\nFROM ip_rib r1\nJOIN bgp_peers p1 ON p1.hash_id = r1.peer_hash_id\nJOIN routers rt1 ON rt1.hash_id = p1.router_hash_id\nJOIN base_attrs ba ON ba.hash_id = r1.base_attr_hash_id\nWHERE rt1.name = '$rr1' AND r1.iswithdrawn = false\n AND NOT EXISTS (\n SELECT 1 FROM ip_rib r2\n JOIN bgp_peers p2 ON p2.hash_id = r2.peer_hash_id\n JOIN routers rt2 ON rt2.hash_id = p2.router_hash_id\n WHERE rt2.name = '$rr2'\n AND r2.prefix = r1.prefix AND r2.prefix_len = r1.prefix_len\n AND r2.iswithdrawn = false\n )\nORDER BY r1.prefix, r1.prefix_len, ba.local_pref DESC NULLS LAST",
"refId": "A"
}
],
"title": "Prefixes Only on $rr1",
"type": "table"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Prefixes present on RR2 but NOT on RR1. Expected: RR1's own locally-originated routes won't appear here. Unexpected entries indicate a convergence or session issue.",
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"},"custom": {"align": "auto","displayMode": "auto","filterable": true},"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]}},
"overrides": [
{"matcher": {"id": "byName","options": "Learned From"},"properties": [{"id": "custom.width","value": 130}]},
{"matcher": {"id": "byName","options": "Origin AS"},"properties": [{"id": "custom.width","value": 90}]},
{"matcher": {"id": "byName","options": "Next Hop"},"properties": [{"id": "custom.width","value": 140}]}
]
},
"gridPos": {"h": 10,"w": 12,"x": 12,"y": 10},
"id": 22,
"options": {"footer": {"fields": "","reducer": ["count"],"show": true},"showHeader": true,"sortBy": [{"desc": false,"displayName": "Prefix"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT DISTINCT ON (r2.prefix, r2.prefix_len)\n r2.prefix::text AS \"Prefix\",\n ba.origin_as AS \"Origin AS\",\n ba.next_hop::text AS \"Next Hop\",\n ba.as_path::text AS \"AS Path\",\n p2.peer_addr::text AS \"Learned From\"\nFROM ip_rib r2\nJOIN bgp_peers p2 ON p2.hash_id = r2.peer_hash_id\nJOIN routers rt2 ON rt2.hash_id = p2.router_hash_id\nJOIN base_attrs ba ON ba.hash_id = r2.base_attr_hash_id\nWHERE rt2.name = '$rr2' AND r2.iswithdrawn = false\n AND NOT EXISTS (\n SELECT 1 FROM ip_rib r1\n JOIN bgp_peers p1 ON p1.hash_id = r1.peer_hash_id\n JOIN routers rt1 ON rt1.hash_id = p1.router_hash_id\n WHERE rt1.name = '$rr1'\n AND r1.prefix = r2.prefix AND r1.prefix_len = r2.prefix_len\n AND r1.iswithdrawn = false\n )\nORDER BY r2.prefix, r2.prefix_len, ba.local_pref DESC NULLS LAST",
"refId": "A"
}
],
"title": "Prefixes Only on $rr2",
"type": "table"
},
{
"collapsed": false,
"gridPos": {"h": 1,"w": 24,"x": 0,"y": 20},
"id": 30,
"title": "Attribute Differences (Same Prefix, Different Best Path)",
"type": "row"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Prefixes present on both RRs but where the selected best path differs in next-hop, AS path, local-pref, or MED. This is normal for multi-homed link subnets where each RR selects a different best path based on router-id tiebreaker. Filter by address family using the AFI dropdown.",
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"},"custom": {"align": "auto","displayMode": "auto","filterable": true},"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]}},
"overrides": [
{"matcher": {"id": "byName","options": "RR1 Next Hop"},"properties": [{"id": "custom.displayMode","value": "color-text"},{"id": "color","value": {"mode": "fixed","fixedColor": "blue"}}]},
{"matcher": {"id": "byName","options": "RR2 Next Hop"},"properties": [{"id": "custom.displayMode","value": "color-text"},{"id": "color","value": {"mode": "fixed","fixedColor": "green"}}]},
{"matcher": {"id": "byName","options": "Diff Type"},"properties": [{"id": "custom.displayMode","value": "color-background"},{"id": "color","value": {"mode": "thresholds"}},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "yellow","value": null}]}}]}
]
},
"gridPos": {"h": 12,"w": 24,"x": 0,"y": 21},
"id": 31,
"options": {"footer": {"fields": "","reducer": ["count"],"show": true},"showHeader": true,"sortBy": [{"desc": false,"displayName": "Prefix"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH c1 AS (\n SELECT DISTINCT ON (r.prefix, r.prefix_len)\n r.prefix, r.prefix_len, r.isipv4,\n ba.next_hop, ba.as_path, ba.origin_as,\n COALESCE(ba.local_pref, 0) AS lp, COALESCE(ba.med, 0) AS med,\n ba.community_list, p.peer_addr AS learned_from\n FROM ip_rib r\n JOIN bgp_peers p ON p.hash_id = r.peer_hash_id\n JOIN routers rt ON rt.hash_id = p.router_hash_id\n JOIN base_attrs ba ON ba.hash_id = r.base_attr_hash_id\n WHERE rt.name = '$rr1' AND r.iswithdrawn = false\n ORDER BY r.prefix, r.prefix_len, ba.local_pref DESC NULLS LAST\n),\nc2 AS (\n SELECT DISTINCT ON (r.prefix, r.prefix_len)\n r.prefix, r.prefix_len, r.isipv4,\n ba.next_hop, ba.as_path, ba.origin_as,\n COALESCE(ba.local_pref, 0) AS lp, COALESCE(ba.med, 0) AS med,\n ba.community_list, p.peer_addr AS learned_from\n FROM ip_rib r\n JOIN bgp_peers p ON p.hash_id = r.peer_hash_id\n JOIN routers rt ON rt.hash_id = p.router_hash_id\n JOIN base_attrs ba ON ba.hash_id = r.base_attr_hash_id\n WHERE rt.name = '$rr2' AND r.iswithdrawn = false\n ORDER BY r.prefix, r.prefix_len, ba.local_pref DESC NULLS LAST\n)\nSELECT\n c1.prefix::text AS \"Prefix\",\n CASE WHEN c1.isipv4 THEN 'IPv4' ELSE 'IPv6' END AS \"AFI\",\n c1.next_hop::text AS \"RR1 Next Hop\",\n c2.next_hop::text AS \"RR2 Next Hop\",\n c1.as_path::text AS \"RR1 AS Path\",\n c2.as_path::text AS \"RR2 AS Path\",\n c1.lp AS \"RR1 LP\",\n c2.lp AS \"RR2 LP\",\n c1.med AS \"RR1 MED\",\n c2.med AS \"RR2 MED\",\n CASE\n WHEN c1.next_hop != c2.next_hop AND c1.as_path != c2.as_path THEN 'NH+ASPath'\n WHEN c1.next_hop != c2.next_hop THEN 'Next-Hop'\n WHEN c1.as_path != c2.as_path THEN 'AS Path'\n WHEN c1.lp != c2.lp THEN 'Local-Pref'\n WHEN c1.med != c2.med THEN 'MED'\n ELSE 'Other'\n END AS \"Diff Type\"\nFROM c1 JOIN c2 ON c1.prefix = c2.prefix AND c1.prefix_len = c2.prefix_len\nWHERE (c1.next_hop != c2.next_hop OR c1.as_path != c2.as_path\n OR c1.lp != c2.lp OR c1.med != c2.med)\n AND ('$afi' = 'All' OR ('$afi' = 'IPv4' AND c1.isipv4 = true) OR ('$afi' = 'IPv6' AND c1.isipv4 = false))\nORDER BY c1.prefix",
"refId": "A"
}
],
"title": "Attribute Differences — $rr1 vs $rr2",
"type": "table"
},
{
"collapsed": false,
"gridPos": {"h": 1,"w": 24,"x": 0,"y": 33},
"id": 40,
"title": "Per-Client Consistency",
"type": "row"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Route counts each RR client sends to each RR. In a healthy cluster, every client should send the same number of routes to both RRs. Mismatches indicate session issues, policy differences, or BMP reporting gaps.",
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"},"custom": {"align": "auto","displayMode": "auto","filterable": true},"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]}},
"overrides": [
{"matcher": {"id": "byName","options": "Delta"},"properties": [{"id": "custom.displayMode","value": "color-background"},{"id": "color","value": {"mode": "thresholds"}},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 5}]}}]},
{"matcher": {"id": "byName","options": "Status"},"properties": [{"id": "custom.displayMode","value": "color-background"},{"id": "mappings","value": [{"options": {"Match": {"color": "green","index": 0,"text": "Match"},"MISMATCH": {"color": "red","index": 1,"text": "MISMATCH"}},"type": "value"}]}]}
]
},
"gridPos": {"h": 10,"w": 12,"x": 0,"y": 34},
"id": 41,
"options": {"footer": {"fields": "","reducer": ["sum"],"show": false},"showHeader": true,"sortBy": [{"desc": true,"displayName": "Delta"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH rr1_peers AS (\n SELECT p.peer_addr, p.hash_id\n FROM bgp_peers p\n JOIN routers rt ON rt.hash_id = p.router_hash_id\n WHERE rt.name = '$rr1'\n),\nrr2_peers AS (\n SELECT p.peer_addr, p.hash_id\n FROM bgp_peers p\n JOIN routers rt ON rt.hash_id = p.router_hash_id\n WHERE rt.name = '$rr2'\n),\nrr1_counts AS (\n SELECT pp.peer_addr, COUNT(*) AS cnt\n FROM rr1_peers pp\n JOIN ip_rib r ON r.peer_hash_id = pp.hash_id AND r.iswithdrawn = false\n GROUP BY pp.peer_addr\n),\nrr2_counts AS (\n SELECT pp.peer_addr, COUNT(*) AS cnt\n FROM rr2_peers pp\n JOIN ip_rib r ON r.peer_hash_id = pp.hash_id AND r.iswithdrawn = false\n GROUP BY pp.peer_addr\n)\nSELECT\n COALESCE(c1.peer_addr, c2.peer_addr)::text AS \"Client\",\n COALESCE(c1.cnt, 0) AS \"Routes to RR1\",\n COALESCE(c2.cnt, 0) AS \"Routes to RR2\",\n ABS(COALESCE(c1.cnt, 0) - COALESCE(c2.cnt, 0)) AS \"Delta\",\n CASE WHEN COALESCE(c1.cnt, 0) = COALESCE(c2.cnt, 0) THEN 'Match' ELSE 'MISMATCH' END AS \"Status\"\nFROM rr1_counts c1\nFULL OUTER JOIN rr2_counts c2 ON c1.peer_addr = c2.peer_addr\nWHERE c1.peer_addr IS NOT NULL AND c2.peer_addr IS NOT NULL\nORDER BY ABS(COALESCE(c1.cnt, 0) - COALESCE(c2.cnt, 0)) DESC, COALESCE(c1.peer_addr, c2.peer_addr)",
"refId": "A"
}
],
"title": "Per-Client Route Consistency",
"type": "table"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "For clients with route count mismatches, shows which specific prefixes differ. Select a client from the Client dropdown to drill down.",
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"},"custom": {"align": "auto","displayMode": "auto","filterable": true},"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]}},
"overrides": [
{"matcher": {"id": "byName","options": "Present On"},"properties": [{"id": "custom.displayMode","value": "color-text"},{"id": "color","value": {"mode": "thresholds"}},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "blue","value": null}]}}]}
]
},
"gridPos": {"h": 10,"w": 12,"x": 12,"y": 34},
"id": 42,
"options": {"footer": {"fields": "","reducer": ["count"],"show": true},"showHeader": true,"sortBy": [{"desc": false,"displayName": "Prefix"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "WITH rr1_client_routes AS (\n SELECT r.prefix, r.prefix_len\n FROM ip_rib r\n JOIN bgp_peers p ON p.hash_id = r.peer_hash_id\n JOIN routers rt ON rt.hash_id = p.router_hash_id\n WHERE rt.name = '$rr1' AND p.peer_addr::text = '$client'\n AND r.iswithdrawn = false\n),\nrr2_client_routes AS (\n SELECT r.prefix, r.prefix_len\n FROM ip_rib r\n JOIN bgp_peers p ON p.hash_id = r.peer_hash_id\n JOIN routers rt ON rt.hash_id = p.router_hash_id\n WHERE rt.name = '$rr2' AND p.peer_addr::text = '$client'\n AND r.iswithdrawn = false\n)\nSELECT r1.prefix::text AS \"Prefix\", 'RR1 only' AS \"Present On\"\nFROM rr1_client_routes r1\nWHERE NOT EXISTS (\n SELECT 1 FROM rr2_client_routes r2\n WHERE r2.prefix = r1.prefix AND r2.prefix_len = r1.prefix_len\n)\nUNION ALL\nSELECT r2.prefix::text AS \"Prefix\", 'RR2 only' AS \"Present On\"\nFROM rr2_client_routes r2\nWHERE NOT EXISTS (\n SELECT 1 FROM rr1_client_routes r1\n WHERE r1.prefix = r2.prefix AND r1.prefix_len = r2.prefix_len\n)\nORDER BY \"Prefix\"",
"refId": "A"
}
],
"title": "Client Prefix Diff — $client",
"type": "table"
},
{
"collapsed": false,
"gridPos": {"h": 1,"w": 24,"x": 0,"y": 44},
"id": 50,
"title": "Full RIB Comparison (All Paths)",
"type": "row"
},
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"description": "Side-by-side view of all paths for a specific prefix on both RRs. Select a prefix from the Prefix dropdown to drill down.",
"fieldConfig": {
"defaults": {"color": {"mode": "thresholds"},"custom": {"align": "auto","displayMode": "auto","filterable": true},"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null}]}},
"overrides": [
{"matcher": {"id": "byName","options": "Router"},"properties": [{"id": "custom.displayMode","value": "color-text"},{"id": "color","value": {"mode": "thresholds"}},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "blue","value": null}]}}]}
]
},
"gridPos": {"h": 10,"w": 24,"x": 0,"y": 45},
"id": 51,
"options": {"footer": {"fields": "","reducer": ["count"],"show": true},"showHeader": true,"sortBy": [{"desc": false,"displayName": "Router"}]},
"targets": [
{
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"format": "table",
"rawSql": "SELECT\n rt.name AS \"Router\",\n p.peer_addr::text AS \"Learned From\",\n ba.next_hop::text AS \"Next Hop\",\n ba.as_path::text AS \"AS Path\",\n ba.origin_as AS \"Origin AS\",\n COALESCE(ba.local_pref, 0) AS \"Local Pref\",\n COALESCE(ba.med, 0) AS \"MED\",\n ba.community_list::text AS \"Communities\",\n ba.cluster_list::text AS \"Cluster List\",\n ba.originator_id::text AS \"Originator ID\",\n r.labels AS \"Labels\",\n r.timestamp AS \"Last Update\"\nFROM ip_rib r\nJOIN bgp_peers p ON p.hash_id = r.peer_hash_id\nJOIN routers rt ON rt.hash_id = p.router_hash_id\nJOIN base_attrs ba ON ba.hash_id = r.base_attr_hash_id\nWHERE rt.name IN ('$rr1', '$rr2')\n AND r.iswithdrawn = false\n AND r.prefix::text = '$prefix'\nORDER BY rt.name, p.peer_addr",
"refId": "A"
}
],
"title": "All Paths for $prefix",
"type": "table"
}
],
"refresh": "30s",
"schemaVersion": 38,
"tags": ["openbmp", "rr-diff", "bgp"],
"templating": {
"list": [
{
"current": {},
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"definition": "SELECT name FROM routers WHERE state = 'up' ORDER BY name",
"hide": 0,
"includeAll": false,
"label": "RR1",
"multi": false,
"name": "rr1",
"options": [],
"query": "SELECT name FROM routers WHERE state = 'up' ORDER BY name",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {},
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"definition": "SELECT name FROM routers WHERE state = 'up' ORDER BY name",
"hide": 0,
"includeAll": false,
"label": "RR2",
"multi": false,
"name": "rr2",
"options": [],
"query": "SELECT name FROM routers WHERE state = 'up' ORDER BY name",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {"selected": true,"text": "All","value": "All"},
"hide": 0,
"includeAll": false,
"label": "AFI",
"multi": false,
"name": "afi",
"options": [
{"selected": true,"text": "All","value": "All"},
{"selected": false,"text": "IPv4","value": "IPv4"},
{"selected": false,"text": "IPv6","value": "IPv6"}
],
"query": "All,IPv4,IPv6",
"skipUrlSync": false,
"type": "custom"
},
{
"current": {},
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"definition": "SELECT DISTINCT p.peer_addr::text FROM bgp_peers p JOIN routers rt ON rt.hash_id = p.router_hash_id WHERE rt.name IN ('$rr1', '$rr2') AND p.state = 'up' ORDER BY 1",
"hide": 0,
"includeAll": false,
"label": "Client",
"multi": false,
"name": "client",
"options": [],
"query": "SELECT DISTINCT p.peer_addr::text FROM bgp_peers p JOIN routers rt ON rt.hash_id = p.router_hash_id WHERE rt.name IN ('$rr1', '$rr2') AND p.state = 'up' ORDER BY 1",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {},
"datasource": {"type": "postgres","uid": "obmp_postgres"},
"definition": "SELECT DISTINCT r.prefix::text FROM ip_rib r JOIN bgp_peers p ON p.hash_id = r.peer_hash_id JOIN routers rt ON rt.hash_id = p.router_hash_id WHERE rt.name IN ('$rr1', '$rr2') AND r.iswithdrawn = false ORDER BY 1",
"hide": 0,
"includeAll": false,
"label": "Prefix",
"multi": false,
"name": "prefix",
"options": [],
"query": "SELECT DISTINCT r.prefix::text FROM ip_rib r JOIN bgp_peers p ON p.hash_id = r.peer_hash_id JOIN routers rt ON rt.hash_id = p.router_hash_id WHERE rt.name IN ('$rr1', '$rr2') AND r.iswithdrawn = false ORDER BY 1",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
}
]
},
"time": {"from": "now-6h","to": "now"},
"timepicker": {},
"timezone": "",
"title": "RR Loc-RIB Diff",
"uid": "rr-locrib-diff",
"version": 1
}