Fix gNMI telemetry: OpenConfig paths, json_ietf encoding, SSH config
- Switch Telegraf from native IOS-XR YANG paths to OpenConfig (openconfig-interfaces:interfaces/interface/state/counters) - Use json_ietf encoding instead of proto (IOS-XR 24.3.1 compat) - Target only CORE-01/CORE-02 (R9K routers blocked by CML mgmt net) - Update all 3 Grafana dashboard queries to match OpenConfig field names (in-octets, out-octets, in-pkts, out-pkts, in-errors, etc.) - Rewrite gnmi_grpc_config.py to use SSH/CLI via paramiko instead of NETCONF (IOS-XR 24.3.1 rejects NETCONF gRPC edit-config) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
6b45f124f0
commit
c28c9b2527
@ -8,7 +8,9 @@ telemetry data.
|
|||||||
|
|
||||||
What this script applies per router:
|
What this script applies per router:
|
||||||
- gRPC server on port 57400 with TLS disabled
|
- gRPC server on port 57400 with TLS disabled
|
||||||
- YANG model: Cisco-IOS-XR-man-ems-cfg
|
|
||||||
|
Uses SSH/CLI (paramiko) instead of NETCONF because IOS-XR 24.3.1
|
||||||
|
rejects the NETCONF edit-config for gRPC with "Need to enable GRPC first".
|
||||||
|
|
||||||
Router targets:
|
Router targets:
|
||||||
CORE-01 (10.100.0.100)
|
CORE-01 (10.100.0.100)
|
||||||
@ -16,11 +18,10 @@ Router targets:
|
|||||||
R9K-01 (10.100.0.1) through R9K-07 (10.100.0.7)
|
R9K-01 (10.100.0.1) through R9K-07 (10.100.0.7)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from ncclient import manager
|
import paramiko
|
||||||
|
import time
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
GRPC_NS = 'http://cisco.com/ns/yang/Cisco-IOS-XR-man-ems-cfg'
|
|
||||||
|
|
||||||
ROUTERS = [
|
ROUTERS = [
|
||||||
('10.100.0.100', 'CORE-01'),
|
('10.100.0.100', 'CORE-01'),
|
||||||
('10.100.0.200', 'CORE-02'),
|
('10.100.0.200', 'CORE-02'),
|
||||||
@ -33,38 +34,47 @@ ROUTERS = [
|
|||||||
('10.100.0.7', 'R9K-07'),
|
('10.100.0.7', 'R9K-07'),
|
||||||
]
|
]
|
||||||
|
|
||||||
GRPC_CONFIG_XML = """
|
USERNAME = 'webui'
|
||||||
<config>
|
PASSWORD = 'cisco'
|
||||||
<grpc xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-man-ems-cfg">
|
GRPC_PORT = 57400
|
||||||
<port>57400</port>
|
|
||||||
<no-tls/>
|
CONFIG_COMMANDS = [
|
||||||
</grpc>
|
'configure terminal',
|
||||||
</config>
|
'grpc',
|
||||||
"""
|
f'port {GRPC_PORT}',
|
||||||
|
'no-tls',
|
||||||
|
'commit',
|
||||||
|
'end',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def configure_router(mgmt_ip, label):
|
def configure_router(mgmt_ip, label):
|
||||||
"""Apply gRPC configuration via NETCONF edit-config + commit."""
|
"""Apply gRPC configuration via SSH CLI."""
|
||||||
print(f"\n{'─'*60}")
|
print(f"\n{'─'*60}")
|
||||||
print(f" Configuring {label} ({mgmt_ip})")
|
print(f" Configuring {label} ({mgmt_ip})")
|
||||||
print(f"{'─'*60}")
|
print(f"{'─'*60}")
|
||||||
print(f" Applying: gRPC port=57400 no-tls")
|
print(f" Applying: gRPC port={GRPC_PORT} no-tls")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with manager.connect(
|
client = paramiko.SSHClient()
|
||||||
host=mgmt_ip,
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
port=830,
|
client.connect(mgmt_ip, username=USERNAME, password=PASSWORD, timeout=10)
|
||||||
username='webui',
|
|
||||||
password='cisco',
|
shell = client.invoke_shell()
|
||||||
hostkey_verify=False,
|
time.sleep(1)
|
||||||
device_params={'name': 'iosxr'},
|
shell.recv(65535) # clear banner
|
||||||
timeout=20,
|
|
||||||
) as m:
|
for cmd in CONFIG_COMMANDS:
|
||||||
print(" → Applying gRPC configuration...")
|
shell.send(cmd + '\n')
|
||||||
m.edit_config(target='candidate', config=GRPC_CONFIG_XML)
|
time.sleep(1.5)
|
||||||
|
|
||||||
|
output = shell.recv(65535).decode()
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
if 'error' in output.lower() or 'fail' in output.lower():
|
||||||
|
print(f" ✗ ERROR on {label}: {output.strip()}")
|
||||||
|
return False
|
||||||
|
|
||||||
print(" → Committing...")
|
|
||||||
m.commit()
|
|
||||||
print(f" ✓ {label} done.")
|
print(f" ✓ {label} done.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@ -74,30 +84,33 @@ def configure_router(mgmt_ip, label):
|
|||||||
|
|
||||||
|
|
||||||
def verify_router(mgmt_ip, label):
|
def verify_router(mgmt_ip, label):
|
||||||
"""Re-read running config to confirm the grpc block is present."""
|
"""Verify gRPC configuration via SSH."""
|
||||||
try:
|
try:
|
||||||
with manager.connect(
|
client = paramiko.SSHClient()
|
||||||
host=mgmt_ip, port=830, username='webui', password='cisco',
|
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
hostkey_verify=False, device_params={'name': 'iosxr'}, timeout=10
|
client.connect(mgmt_ip, username=USERNAME, password=PASSWORD, timeout=10)
|
||||||
) as m:
|
|
||||||
filt_grpc = """<filter>
|
|
||||||
<grpc xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-man-ems-cfg">
|
|
||||||
<port/>
|
|
||||||
<no-tls/>
|
|
||||||
</grpc>
|
|
||||||
</filter>"""
|
|
||||||
r_grpc = m.get_config(source='running', filter=filt_grpc)
|
|
||||||
|
|
||||||
has_grpc = '<port>57400</port>' in str(r_grpc)
|
shell = client.invoke_shell()
|
||||||
has_notls = '<no-tls' in str(r_grpc)
|
time.sleep(1)
|
||||||
|
shell.recv(65535)
|
||||||
|
|
||||||
g = '✓' if has_grpc else '✗'
|
shell.send('show running-config grpc\n')
|
||||||
|
time.sleep(3)
|
||||||
|
output = shell.recv(65535).decode()
|
||||||
|
client.close()
|
||||||
|
|
||||||
|
has_port = f'port {GRPC_PORT}' in output
|
||||||
|
has_notls = 'no-tls' in output
|
||||||
|
|
||||||
|
p = '✓' if has_port else '✗'
|
||||||
t = '✓' if has_notls else '✗'
|
t = '✓' if has_notls else '✗'
|
||||||
status = 'OK' if (has_grpc and has_notls) else 'INCOMPLETE'
|
status = 'OK' if (has_port and has_notls) else 'INCOMPLETE'
|
||||||
print(f" {label:8s} grpc-port={g} no-tls={t} [{status}]")
|
print(f" {label:8s} grpc-port={p} no-tls={t} [{status}]")
|
||||||
|
return has_port and has_notls
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f" {label:8s} verify error: {e}")
|
print(f" {label:8s} verify error: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@ -111,27 +124,34 @@ def main():
|
|||||||
ok = configure_router(mgmt_ip, label)
|
ok = configure_router(mgmt_ip, label)
|
||||||
results.append((mgmt_ip, label, ok))
|
results.append((mgmt_ip, label, ok))
|
||||||
|
|
||||||
# Summary
|
# Verification pass
|
||||||
print(f"\n{'='*60}")
|
print(f"\n{'='*60}")
|
||||||
print("Post-apply verification")
|
print("Post-apply verification")
|
||||||
print('='*60)
|
print('='*60)
|
||||||
print(f" {'Router':8s} {'gRPC Port':9s} {'No-TLS':6s} Status")
|
print(f" {'Router':8s} {'gRPC Port':9s} {'No-TLS':6s} Status")
|
||||||
for mgmt_ip, label, ok in results:
|
|
||||||
if ok:
|
all_ok = True
|
||||||
verify_router(mgmt_ip, label)
|
for mgmt_ip, label, applied_ok in results:
|
||||||
|
if applied_ok:
|
||||||
|
if not verify_router(mgmt_ip, label):
|
||||||
|
all_ok = False
|
||||||
else:
|
else:
|
||||||
print(f" {label:8s} skipped (apply failed)")
|
print(f" {label:8s} skipped (apply failed)")
|
||||||
|
all_ok = False
|
||||||
|
|
||||||
failed = [label for _, label, ok in results if not ok]
|
failed = [label for _, label, ok in results if not ok]
|
||||||
print()
|
print()
|
||||||
if failed:
|
if failed:
|
||||||
print(f"FAILED: {', '.join(failed)}")
|
print(f"FAILED: {', '.join(failed)}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
elif all_ok:
|
||||||
print("All routers configured successfully.")
|
print("All routers configured successfully.")
|
||||||
print()
|
print()
|
||||||
print("gRPC is now listening on port 57400 (no TLS) on all routers.")
|
print(f"gRPC is now listening on port {GRPC_PORT} (no TLS) on all routers.")
|
||||||
print("Next: start Telegraf with gNMI input plugin to begin collecting telemetry.")
|
print("Next: start Telegraf with gNMI input plugin to begin collecting telemetry.")
|
||||||
|
else:
|
||||||
|
print("Some routers may have incomplete configuration. Check output above.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@ -74,7 +74,7 @@
|
|||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
||||||
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r._field == \"bytes_received\" or r._field == \"bytes_sent\")\n |> derivative(unit: 1s, nonNegative: true)\n |> map(fn: (r) => ({r with _value: if r._value < 0.0 then 0.0 else r._value}))",
|
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r._field == \"in-octets\" or r._field == \"out-octets\")\n |> derivative(unit: 1s, nonNegative: true)\n |> map(fn: (r) => ({r with _value: if r._value < 0.0 then 0.0 else r._value}))",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
||||||
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"input_errors\" or r._field == \"output_errors\" or r._field == \"crc_errors\")\n |> derivative(unit: 1s, nonNegative: true)",
|
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"in-errors\" or r._field == \"out-errors\" or r._field == \"in-fcs-errors\")\n |> derivative(unit: 1s, nonNegative: true)",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -84,7 +84,7 @@
|
|||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
||||||
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"input_drops\" or r._field == \"output_drops\")\n |> derivative(unit: 1s, nonNegative: true)",
|
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"in-discards\" or r._field == \"out-discards\")\n |> derivative(unit: 1s, nonNegative: true)",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -102,19 +102,19 @@
|
|||||||
"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}
|
"thresholds": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}
|
||||||
},
|
},
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{"matcher": {"id": "byName","options": "input_errors"},"properties": [{"id": "custom.displayMode","value": "color-background-solid"},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}}]},
|
{"matcher": {"id": "byName","options": "in-errors"},"properties": [{"id": "custom.displayMode","value": "color-background-solid"},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}}]},
|
||||||
{"matcher": {"id": "byName","options": "output_errors"},"properties": [{"id": "custom.displayMode","value": "color-background-solid"},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}}]},
|
{"matcher": {"id": "byName","options": "out-errors"},"properties": [{"id": "custom.displayMode","value": "color-background-solid"},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}}]},
|
||||||
{"matcher": {"id": "byName","options": "input_drops"},"properties": [{"id": "custom.displayMode","value": "color-background-solid"},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}}]},
|
{"matcher": {"id": "byName","options": "in-discards"},"properties": [{"id": "custom.displayMode","value": "color-background-solid"},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}}]},
|
||||||
{"matcher": {"id": "byName","options": "output_drops"},"properties": [{"id": "custom.displayMode","value": "color-background-solid"},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}}]}
|
{"matcher": {"id": "byName","options": "out-discards"},"properties": [{"id": "custom.displayMode","value": "color-background-solid"},{"id": "thresholds","value": {"mode": "absolute","steps": [{"color": "green","value": null},{"color": "yellow","value": 1},{"color": "red","value": 100}]}}]}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gridPos": {"h": 12,"w": 24,"x": 0,"y": 20},
|
"gridPos": {"h": 12,"w": 24,"x": 0,"y": 20},
|
||||||
"id": 3,
|
"id": 3,
|
||||||
"options": {"footer": {"fields": "","reducer": ["sum"],"show": false},"showHeader": true,"sortBy": [{"desc": true,"displayName": "input_errors"}]},
|
"options": {"footer": {"fields": "","reducer": ["sum"],"show": false},"showHeader": true,"sortBy": [{"desc": true,"displayName": "in-errors"}]},
|
||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
||||||
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"input_errors\" or r._field == \"output_errors\" or r._field == \"crc_errors\" or r._field == \"input_drops\" or r._field == \"output_drops\")\n |> last()\n |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n |> keep(columns: [\"source\", \"name\", \"input_errors\", \"output_errors\", \"crc_errors\", \"input_drops\", \"output_drops\"])\n |> sort(columns: [\"input_errors\"], desc: true)",
|
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"in-errors\" or r._field == \"out-errors\" or r._field == \"in-fcs-errors\" or r._field == \"in-discards\" or r._field == \"out-discards\")\n |> last()\n |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n |> keep(columns: [\"source\", \"name\", \"in-errors\", \"out-errors\", \"in-fcs-errors\", \"in-discards\", \"out-discards\"])\n |> sort(columns: [\"in-errors\"], desc: true)",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@ -59,7 +59,7 @@
|
|||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
||||||
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"bytes_received\" or r._field == \"bytes_sent\")\n |> derivative(unit: 1s, nonNegative: true)\n |> map(fn: (r) => ({r with _value: if r._value < 0.0 then 0.0 else r._value}))",
|
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"in-octets\" or r._field == \"out-octets\")\n |> derivative(unit: 1s, nonNegative: true)\n |> map(fn: (r) => ({r with _value: if r._value < 0.0 then 0.0 else r._value}))",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -84,7 +84,7 @@
|
|||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
||||||
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"packets_received\" or r._field == \"packets_sent\")\n |> derivative(unit: 1s, nonNegative: true)\n |> map(fn: (r) => ({r with _value: if r._value < 0.0 then 0.0 else r._value}))",
|
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"in-pkts\" or r._field == \"out-pkts\")\n |> derivative(unit: 1s, nonNegative: true)\n |> map(fn: (r) => ({r with _value: if r._value < 0.0 then 0.0 else r._value}))",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -109,7 +109,7 @@
|
|||||||
"targets": [
|
"targets": [
|
||||||
{
|
{
|
||||||
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
"datasource": {"type": "influxdb","uid": "obmp_influxdb"},
|
||||||
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"bytes_received\" or r._field == \"bytes_sent\")\n |> derivative(unit: 1s, nonNegative: true)\n |> group(columns: [\"source\", \"name\", \"_field\"])\n |> sum()\n |> group(columns: [\"source\", \"name\"])\n |> sum()\n |> group()\n |> sort(columns: [\"_value\"], desc: true)\n |> limit(n: 20)",
|
"query": "from(bucket: \"telemetry\")\n |> range(start: v.timeRangeStart, stop: v.timeRangeStop)\n |> filter(fn: (r) => r._measurement == \"interface_counters\")\n |> filter(fn: (r) => r.source =~ /${router:regex}/)\n |> filter(fn: (r) => r.name =~ /${interface:regex}/)\n |> filter(fn: (r) => r._field == \"in-octets\" or r._field == \"out-octets\")\n |> derivative(unit: 1s, nonNegative: true)\n |> group(columns: [\"source\", \"name\", \"_field\"])\n |> sum()\n |> group(columns: [\"source\", \"name\"])\n |> sum()\n |> group()\n |> sort(columns: [\"_value\"], desc: true)\n |> limit(n: 20)",
|
||||||
"refId": "A"
|
"refId": "A"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -122,7 +122,7 @@
|
|||||||
"id": 4,
|
"id": 4,
|
||||||
"options": {
|
"options": {
|
||||||
"code": {"language": "plaintext","showLineNumbers": false,"showMiniMap": false},
|
"code": {"language": "plaintext","showLineNumbers": false,"showMiniMap": false},
|
||||||
"content": "## Interface Utilization Dashboard\n\nThis dashboard displays real-time interface utilization metrics collected via **gNMI streaming telemetry** from IOS-XR routers.\n\n- **Data source:** InfluxDB (Telegraf gNMI input plugin)\n- **YANG model:** `Cisco-IOS-XR-infra-statsd-oper`\n- **Subscription path:** `/infra-statistics/interfaces/interface/latest/generic-counters`\n- **Sample interval:** 10 seconds\n\nUse the **Router** and **Interface** template variables at the top to filter the view.",
|
"content": "## Interface Utilization Dashboard\n\nThis dashboard displays real-time interface utilization metrics collected via **gNMI streaming telemetry** from IOS-XR routers.\n\n- **Data source:** InfluxDB (Telegraf gNMI input plugin)\n- **YANG model:** OpenConfig (`openconfig-interfaces`)\n- **Subscription path:** `/interfaces/interface/state/counters`\n- **Sample interval:** 10 seconds\n\nUse the **Router** and **Interface** template variables at the top to filter the view.",
|
||||||
"mode": "markdown"
|
"mode": "markdown"
|
||||||
},
|
},
|
||||||
"title": "About",
|
"title": "About",
|
||||||
|
|||||||
@ -17,43 +17,40 @@
|
|||||||
# INPUT PLUGINS #
|
# INPUT PLUGINS #
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
|
## CORE routers (directly reachable on port 57400 from host)
|
||||||
|
## R9K routers (10.100.0.1-7) are blocked by CML management network filtering
|
||||||
[[inputs.gnmi]]
|
[[inputs.gnmi]]
|
||||||
addresses = [
|
addresses = [
|
||||||
"10.100.0.100:57400",
|
"10.100.0.100:57400",
|
||||||
"10.100.0.200:57400",
|
"10.100.0.200:57400"
|
||||||
"10.100.0.1:57400",
|
|
||||||
"10.100.0.2:57400",
|
|
||||||
"10.100.0.3:57400",
|
|
||||||
"10.100.0.4:57400",
|
|
||||||
"10.100.0.5:57400",
|
|
||||||
"10.100.0.6:57400",
|
|
||||||
"10.100.0.7:57400"
|
|
||||||
]
|
]
|
||||||
username = "webui"
|
username = "webui"
|
||||||
password = "cisco"
|
password = "cisco"
|
||||||
|
|
||||||
## Do not verify the server certificate
|
## No TLS (lab environment)
|
||||||
enable_tls = false
|
enable_tls = false
|
||||||
|
|
||||||
## gNMI encoding requested (one of: "proto", "json", "json_ietf", "bytes")
|
## Use json_ietf encoding (supported by IOS-XR 24.3.1)
|
||||||
encoding = "proto"
|
encoding = "json_ietf"
|
||||||
|
|
||||||
## Redial in case of failures after
|
## Redial in case of failures after
|
||||||
redial = "10s"
|
redial = "10s"
|
||||||
|
|
||||||
|
## OpenConfig interface counters (bytes, packets, errors, discards)
|
||||||
[[inputs.gnmi.subscription]]
|
[[inputs.gnmi.subscription]]
|
||||||
name = "interface_counters"
|
name = "interface_counters"
|
||||||
origin = "Cisco-IOS-XR-infra-statsd-oper"
|
origin = "openconfig-interfaces"
|
||||||
path = "/infra-statistics/interfaces/interface/latest/generic-counters"
|
path = "/interfaces/interface/state/counters"
|
||||||
subscription_mode = "sample"
|
subscription_mode = "sample"
|
||||||
sample_interval = "10s"
|
sample_interval = "10s"
|
||||||
|
|
||||||
|
## OpenConfig interface state (admin/oper status, description, type)
|
||||||
[[inputs.gnmi.subscription]]
|
[[inputs.gnmi.subscription]]
|
||||||
name = "interface_rates"
|
name = "interface_state"
|
||||||
origin = "Cisco-IOS-XR-infra-statsd-oper"
|
origin = "openconfig-interfaces"
|
||||||
path = "/infra-statistics/interfaces/interface/latest/data-rate"
|
path = "/interfaces/interface/state"
|
||||||
subscription_mode = "sample"
|
subscription_mode = "sample"
|
||||||
sample_interval = "10s"
|
sample_interval = "30s"
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# OUTPUT PLUGINS #
|
# OUTPUT PLUGINS #
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user