{ "annotations": { "list": [ { "builtIn": 1, "datasource": { "type": "datasource", "uid": "grafana" }, "enable": true, "hide": true, "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" } ] }, "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, "links": [], "liveNow": false, "panels": [ { "datasource": { "type": "postgres", "uid": "obmp_postgres" }, "fieldConfig": { "defaults": { "custom": { "align": "auto", "cellOptions": { "type": "auto" }, "inspect": false } }, "overrides": [ { "matcher": { "id": "byName", "options": "Max BW (B/s)" }, "properties": [{ "id": "unit", "value": "Bps" }] }, { "matcher": { "id": "byName", "options": "Max Reservable BW" }, "properties": [{ "id": "unit", "value": "Bps" }] } ] }, "gridPos": { "h": 10, "w": 24, "x": 0, "y": 0 }, "id": 1, "options": { "showHeader": true, "sortBy": [{ "desc": false, "displayName": "Local Router" }] }, "targets": [ { "datasource": { "type": "postgres", "uid": "obmp_postgres" }, "format": "table", "rawQuery": true, "rawSql": "SELECT local_router_name as \"Local Router\",\n remote_router_name as \"Remote Router\",\n interface_addr::text as \"Interface IP\",\n neighbor_addr::text as \"Neighbor IP\",\n max_link_bw as \"Max BW (B/s)\",\n max_resv_bw as \"Max Reservable BW\",\n unreserved_bw as \"Unreserved BW\",\n igp_metric as \"IGP Metric\",\n te_def_metric as \"TE Metric\"\nFROM v_ls_links\nWHERE peer_hash_id = '$peer_hash' AND iswithdrawn = false\nORDER BY local_router_name, remote_router_name", "refId": "A" } ], "title": "Link Capacity Inventory (from BGP-LS)", "type": "table" }, { "datasource": { "type": "postgres", "uid": "obmp_postgres" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisLabel": "Bandwidth (B/s)", "fillOpacity": 80, "gradientMode": "none", "lineWidth": 1, "scaleDistribution": { "type": "linear" }, "showValue": "auto", "stacking": { "group": "A", "mode": "none" } }, "unit": "Bps" }, "overrides": [] }, "gridPos": { "h": 10, "w": 12, "x": 0, "y": 10 }, "id": 2, "options": { "barRadius": 0.1, "barWidth": 0.8, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", "placement": "bottom" }, "orientation": "horizontal", "tooltip": { "mode": "single", "sort": "none" }, "xTickLabelRotation": 0 }, "targets": [ { "datasource": { "type": "postgres", "uid": "obmp_postgres" }, "format": "table", "rawQuery": true, "rawSql": "SELECT local_router_name || ' -> ' || remote_router_name as \"Link\",\n COALESCE(max_link_bw, 0) as \"Max Bandwidth\",\n COALESCE(max_resv_bw, 0) as \"Max Reservable\",\n COALESCE(max_link_bw, 0) - COALESCE(max_resv_bw, 0) as \"Unreserved Gap\"\nFROM v_ls_links\nWHERE peer_hash_id = '$peer_hash' AND iswithdrawn = false\n AND max_link_bw IS NOT NULL AND max_link_bw > 0\nORDER BY max_link_bw DESC", "refId": "A" } ], "title": "Capacity vs Reservable Bandwidth", "type": "barchart" }, { "datasource": { "type": "postgres", "uid": "obmp_postgres" }, "fieldConfig": { "defaults": { "color": { "mode": "thresholds" }, "thresholds": { "mode": "percentage", "steps": [ { "color": "green", "value": null }, { "color": "yellow", "value": 50 }, { "color": "orange", "value": 75 }, { "color": "red", "value": 90 } ] }, "unit": "percentunit", "max": 1, "min": 0 }, "overrides": [] }, "gridPos": { "h": 10, "w": 12, "x": 12, "y": 10 }, "id": 3, "options": { "minVizHeight": 75, "minVizWidth": 75, "orientation": "auto", "reduceOptions": { "calcs": ["lastNotNull"], "fields": "", "values": true }, "showThresholdLabels": false, "showThresholdMarkers": true, "sizing": "auto" }, "targets": [ { "datasource": { "type": "postgres", "uid": "obmp_postgres" }, "format": "table", "rawQuery": true, "rawSql": "SELECT local_router_name || ' -> ' || remote_router_name as \"Link\",\n CASE WHEN max_link_bw > 0 \n THEN 1.0 - (COALESCE(max_resv_bw, 0)::float / max_link_bw::float)\n ELSE 0 END as \"Reservation Ratio\"\nFROM v_ls_links\nWHERE peer_hash_id = '$peer_hash' AND iswithdrawn = false\n AND max_link_bw IS NOT NULL AND max_link_bw > 0\nORDER BY \"Reservation Ratio\" DESC\nLIMIT 10", "refId": "A" } ], "title": "Bandwidth Reservation Ratio (Higher = More Reserved)", "type": "gauge" }, { "datasource": { "type": "postgres", "uid": "obmp_postgres" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "fillOpacity": 80, "gradientMode": "none", "lineWidth": 1, "showValue": "auto", "stacking": { "group": "A", "mode": "none" } } }, "overrides": [] }, "gridPos": { "h": 8, "w": 12, "x": 0, "y": 20 }, "id": 4, "options": { "barRadius": 0.1, "barWidth": 0.8, "groupWidth": 0.7, "legend": { "calcs": [], "displayMode": "list", "placement": "bottom" }, "orientation": "horizontal", "tooltip": { "mode": "single", "sort": "none" } }, "targets": [ { "datasource": { "type": "postgres", "uid": "obmp_postgres" }, "format": "table", "rawQuery": true, "rawSql": "SELECT local_router_name || ' -> ' || remote_router_name as \"Link\",\n igp_metric as \"IGP Metric\",\n COALESCE(te_def_metric, 0) as \"TE Default Metric\"\nFROM v_ls_links\nWHERE peer_hash_id = '$peer_hash' AND iswithdrawn = false\nORDER BY igp_metric DESC\nLIMIT 20", "refId": "A" } ], "title": "IGP Metric vs TE Default Metric", "type": "barchart" }, { "gridPos": { "h": 8, "w": 12, "x": 12, "y": 20 }, "id": 5, "options": { "mode": "markdown", "content": "## What-If: CSPF Path Computation\n\nIn a real MPLS-TE or SR-TE deployment, the headend router runs **Constrained Shortest Path First (CSPF)** to find paths that satisfy:\n\n1. **Bandwidth constraint** - Enough unreserved BW at the required priority\n2. **Admin group (affinity)** - Link colors must match include/exclude masks\n3. **SRLG diversity** - Backup path avoids shared risk with primary\n4. **TE metric optimization** - Minimize TE metric (not IGP metric)\n\n### How BGP-LS Enables This\n\nBGP-LS distributes the complete IGP topology **with TE attributes** to an external controller (PCE, SDN controller). The controller can:\n\n- Build a Traffic Engineering Database (TED)\n- Run CSPF with arbitrary constraints\n- Program SR-TE policies via PCEP or gRPC\n\n### Data Available in This Lab\n\n| Attribute | Source | Available? |\n|-----------|--------|------------|\n| Topology (nodes/links) | BGP-LS | Yes |\n| IGP Metric | BGP-LS | Yes |\n| TE Default Metric | BGP-LS TLV 1092 | Check TE table |\n| Max Link BW | BGP-LS TLV 1089 | Check TE table |\n| Max Reservable BW | BGP-LS TLV 1090 | Check TE table |\n| Unreserved BW | BGP-LS TLV 1091 | Check TE table |\n| Admin Group | BGP-LS TLV 1088 | Check TE table |\n| SRLG | BGP-LS TLV 1096 | Check TE table |\n| SR Node SID | BGP-LS TLV 1034 | Check SR table |\n| SR Adj SID | BGP-LS TLV 1099 | Check SR table |" }, "title": "CSPF & Traffic Engineering Concepts", "type": "text" }, { "gridPos": { "h": 12, "w": 24, "x": 0, "y": 28 }, "id": 6, "options": { "mode": "markdown", "content": "## Integration Guide: Adding Real-Time Link Utilization\n\nBMP/BGP-LS provides **capacity** data (max bandwidth, reservable bandwidth) but NOT real-time **utilization**. To complete the traffic engineering picture, you need streaming telemetry.\n\n### Architecture\n\n```\n +------------------+\n IOS-XR Routers ---->| OpenBMP Collector|----> PostgreSQL (topology + capacity)\n | +------------------+\n | \n +-- gNMI ----->| Telegraf |----> InfluxDB/Prometheus (utilization)\n +------------------+\n |\n +------------------+\n | Grafana | <-- Mixed datasource queries\n +------------------+\n```\n\n### Step 1: Enable Model-Driven Telemetry on IOS-XR\n\n```\ntelemetry model-driven\n sensor-group INTF-COUNTERS\n sensor-path Cisco-IOS-XR-infra-statsd-oper:infra-statistics/interfaces/interface/latest/generic-counters\n !\n subscription INTF-SUB\n sensor-group-id INTF-COUNTERS sample-interval 30000\n destination-id TELEGRAF\n !\n destination-group TELEGRAF\n address-family ipv4 10.40.40.202 port 57000\n encoding self-describing-gpb\n protocol grpc no-tls\n !\n !\n```\n\n### Step 2: Telegraf Configuration\n\n```toml\n[[inputs.cisco_telemetry_mdt]]\n transport = \"grpc\"\n service_address = \":57000\"\n\n[[outputs.influxdb_v2]]\n urls = [\"http://localhost:8086\"]\n token = \"your-token\"\n organization = \"openbmp\"\n bucket = \"telemetry\"\n```\n\n### Step 3: Grafana Mixed Datasource Query\n\nCombine BGP-LS capacity from PostgreSQL with utilization from InfluxDB:\n\n```\n-- PostgreSQL: Get link capacity\nSELECT interface_addr::text as interface, max_link_bw\nFROM v_ls_links WHERE peer_hash_id = '$peer_hash'\n\n-- InfluxDB: Get interface utilization\nfrom(bucket: \"telemetry\")\n |> range(start: -1h)\n |> filter(fn: (r) => r._measurement == \"Cisco-IOS-XR-infra-statsd-oper\")\n |> filter(fn: (r) => r._field == \"bytes-received\" or r._field == \"bytes-sent\")\n |> derivative(unit: 1s, nonNegative: true)\n```\n\n### Step 4: Calculate Utilization %\n\nIn Grafana, use **Transformations** to:\n1. Join PostgreSQL capacity with InfluxDB utilization by interface IP\n2. Add calculated field: `utilization_pct = bytes_per_sec / max_link_bw * 100`\n3. Set threshold alerts: >80% = warning, >95% = critical\n\n### Key gNMI Sensor Paths for IOS-XR\n\n| Sensor Path | Data |\n|-------------|------|\n| `Cisco-IOS-XR-infra-statsd-oper:infra-statistics/interfaces/interface/latest/generic-counters` | Interface byte/packet counters |\n| `Cisco-IOS-XR-infra-statsd-oper:infra-statistics/interfaces/interface/latest/data-rate` | Current data rate (bits/sec) |\n| `Cisco-IOS-XR-mpls-te-oper:mpls-te/tunnels/summary` | MPLS-TE tunnel summary |\n| `Cisco-IOS-XR-ip-rsvp-oper:rsvp/interface-briefs` | RSVP interface reservations |\n| `Cisco-IOS-XR-segment-routing-ms-oper:srms/policy` | SR-MPLS policy state |\n\n### RFC 8571: Performance Metrics via BGP-LS\n\nIf routers support RFC 8571, these metrics flow through BGP-LS automatically:\n- **Unidirectional Link Delay** (TLV 1114) - microseconds\n- **Min/Max Link Delay** (TLV 1115)\n- **Delay Variation (jitter)** (TLV 1116)\n- **Link Loss** (TLV 1117) - percentage\n- **Residual Bandwidth** (TLV 1118)\n- **Available Bandwidth** (TLV 1119)\n- **Utilized Bandwidth** (TLV 1120)\n\nThese would appear in the `ls_links` table if the OpenBMP parser supports them." }, "title": "Integration Guide: Streaming Telemetry for Link Utilization", "type": "text" } ], "schemaVersion": 39, "tags": ["obmp-learning"], "templating": { "list": [ { "current": {}, "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" } ] }, "time": { "from": "now-6h", "to": "now" }, "timepicker": {}, "timezone": "", "title": "Link Utilization & TE Thought Experiment", "uid": "obmp-learn-10", "version": 1 }