gNMI Subscriptions and Streaming
Objective
In this lesson you will learn how to build and verify gNMI SUBSCRIBE streams using both ON_CHANGE and SAMPLE modes. You will construct gNMI Subscribe RPC requests (JSON_IETF encoding), observe the device responses including the important sync_response, and interpret the telemetry payloads for real-time monitoring. This matters in production because streaming telemetry replaces slow SNMP polls — giving near real-time visibility into interface state, counters, and other operational data used by monitoring and analytics systems.
Real-world scenario: In a campus or data-center network you want immediate notification when a link flap occurs (ON_CHANGE for interface oper state) and periodic interface counters for capacity planning (SAMPLE every 30 seconds). gNMI subscriptions provide both efficiently from IOS XE devices.
Quick Recap
Refer to the topology used in Lesson 1 for device connectivity and IP addressing. This lesson does not add new routers or switches; it focuses on building gNMI subscription requests from a collector (for example, YANG Suite or a custom gNMI client) to the IOS XE device.
Key Concepts
- gNMI SUBSCRIBE RPC: The SUBSCRIBE RPC supports multiple modes — ON_CHANGE and SAMPLE — and returns telemetry updates over a long-lived gRPC stream. The client opens a stream and the device sends incremental updates.
- ON_CHANGE mode: The device sends an update only when the state of the subscribed YANG path changes. Useful for events (e.g., interface up/down). Under the hood the device detects the change and pushes an update to the collector.
- SAMPLE mode: The device sends periodic samples at a configured interval (e.g., 30 seconds). The gNMI configuration carries a sampleInterval value. This is used for counters and performance metrics.
- Encoding and payloads: gNMI supports encodings such as JSON_IETF (text) and PROTO (binary). The reference demonstrates JSON_IETF for subscriptions and notes PROTO for more granular telemetry efficiency.
- sync_response: When a subscription is first established, the device sends a special message indicating that the initial set of existing data has been sent at least once. Collectors use this to know they have a complete baseline before processing delta updates.
Tip: Think of ON_CHANGE like a doorbell (only rings when someone arrives). SAMPLE is like a scheduled camera snapshot taken every 30 seconds.
Step-by-step configuration
Step 1: Inspect device gNMI/MDT capabilities
What we are doing: We query the device for its gNMI/MDT capability information to confirm support for ON_CHANGE and SAMPLE subscriptions and which YANG modules are available (for example, Cisco-IOS-XE-mdt-capabilities-oper and Cisco-IOS-XE-interfaces-oper). This matters so the collector chooses paths and modes the device supports.
{
"rpc": "capabilities",
"encoding": "JSON_IETF"
}
What just happened: The collector sent a gNMI Capabilities RPC over gRPC. The device will respond with supported encodings, RPCs (GET, SET, SUBSCRIBE), and a list of YANG modules (for example, Cisco-IOS-XE-mdt-capabilities-oper and Cisco-IOS-XE-interfaces-oper). Knowing available modules lets you build valid subscription paths.
Real-world note: Always confirm capabilities before requesting telemetry — a mismatch is a common cause of failed subscriptions.
Verify:
{
"capabilities": {
"supported_models": [
{
"name": "Cisco-IOS-XE-mdt-capabilities-oper",
"version": "17.11"
},
{
"name": "Cisco-IOS-XE-interfaces-oper",
"version": "17.11"
}
],
"supported_encodings": [
"JSON_IETF",
"PROTO"
],
"supported_rpcs": [
"GET",
"SET",
"SUBSCRIBE"
]
}
}
Explanation of output: This shows the device supports SUBSCRIBE and JSON_IETF, plus the specific Cisco models required to request interface operational data and MDT capability information.
Step 2: Build an ON_CHANGE subscription for interface operational state
What we are doing: Create a gNMI SUBSCRIBE request in ON_CHANGE mode targeting the interface operational state YANG path (Cisco-IOS-XE-interfaces-oper:interfaces/interface). This gives immediate updates when an interface transitions up/down, which is essential for event-driven monitoring and alerting.
{
"subscribe": {
"subscription_list": {
"mode": "STREAM",
"encoding": "JSON_IETF",
"subscription": [
{
"path": {
"elem": [
{"name": "Cisco-IOS-XE-interfaces-oper:interfaces"},
{"name": "interface"}
]
},
"mode": "ON_CHANGE"
}
]
}
}
}
What just happened: The collector sent a streaming SUBSCRIBE request in ON_CHANGE mode. The device will monitor the specified path and push a telemetry update each time any interface operational data changes (for example, admin/oper status or counters reaching thresholds).
Real-world note: Use ON_CHANGE for event-driven alerting such as BGP session down, interface down, or VRF changes — it reduces bandwidth compared to polling.
Verify:
// Initial stream messages:
{
"update": {
"prefix": {},
"notification": [
{
"timestamp": 1710000000000000,
"update": [
{
"path": {
"elem": [
{"name": "Cisco-IOS-XE-interfaces-oper:interfaces"},
{"name": "interface"},
{"name": "GigabitEthernet0/0"}
]
},
"val": {
"name": "GigabitEthernet0/0",
"admin-status": "up",
"oper-status": "up",
"phys-address": "00:11:22:33:44:55"
}
}
]
}
]
}
}
// Device sends a sync_response to indicate baseline complete
{
"sync_response": {}
}
Explanation: The device first sends the current state of the interface(s) as notification(s). The subsequent sync_response object signals that the device has sent the initial snapshot of existing data. After this, only changes generate notifications.
Step 3: Create a SAMPLE subscription for periodic interface counters
What we are doing: Build a SUBSCRIBE request in SAMPLE mode with a sampleInterval of 30000000000 (30 seconds) to obtain regular interface counters for capacity planning and trend analysis. SAMPLE mode is ideal for periodic metrics, e.g., octets, errors, discards.
{
"subscribe": {
"subscription_list": {
"mode": "STREAM",
"encoding": "JSON_IETF",
"subscription": [
{
"path": {
"elem": [
{"name": "Cisco-IOS-XE-interfaces-oper:interfaces"},
{"name": "interface"}
]
},
"mode": "SAMPLE",
"sample_interval": "30000000000"
}
]
}
}
}
What just happened: The collector requested periodic samples for the interface YANG path. The device will emit notifications containing interface counters on the configured interval (every 30 seconds). The sampleInterval value is in nanoseconds according to gNMI/YANG conventions, so "30000000000" equals 30 seconds.
Real-world note: Choose sample intervals wisely — too frequent increases load and storage; too sparse may miss bursts. In production, many teams use 10–60 second intervals depending on use case.
Verify:
// Example periodic notification every 30 seconds:
{
"update": {
"prefix": {},
"notification": [
{
"timestamp": 1710000010000000,
"update": [
{
"path": {
"elem": [
{"name": "Cisco-IOS-XE-interfaces-oper:interfaces"},
{"name": "interface"},
{"name": "GigabitEthernet0/0"}
]
},
"val": {
"name": "GigabitEthernet0/0",
"in-octets": 123456789,
"out-octets": 98765432,
"in-errors": 0,
"out-errors": 0
}
}
]
}
]
}
}
Explanation: Each JSON notification contains the sampled counters for the given interface. The collector can store or process these records for trend reporting or alerting.
Step 4: Demonstrate ON_CHANGE followed by SAMPLE for hybrid monitoring
What we are doing: Show a combined subscription list that requests ON_CHANGE for oper-state (events) and SAMPLE for counters in the same SUBSCRIBE RPC. This hybrid approach provides immediate event notification and periodic metrics without opening separate streams.
{
"subscribe": {
"subscription_list": {
"mode": "STREAM",
"encoding": "JSON_IETF",
"subscription": [
{
"path": {
"elem": [
{"name": "Cisco-IOS-XE-interfaces-oper:interfaces"},
{"name": "interface"},
{"name": "oper-status"}
]
},
"mode": "ON_CHANGE"
},
{
"path": {
"elem": [
{"name": "Cisco-IOS-XE-interfaces-oper:interfaces"},
{"name": "interface"},
{"name": "counters"}
]
},
"mode": "SAMPLE",
"sample_interval": "30000000000"
}
]
}
}
}
What just happened: The collector opened a single streaming SUBSCRIBE that instructs the device to send immediate updates when oper-status changes and to sample counters every 30 seconds. Using one stream reduces connection overhead and simplifies correlation of events and metrics.
Real-world note: Correlating an ON_CHANGE interface-down event with recent SAMPLE counter trends can help determine if a link failed due to sustained errors or congestion.
Verify:
// Example mixed updates:
{
"update": {
"prefix": {},
"notification": [
{
"timestamp": 1710000020000000,
"update": [
{
"path": {
"elem": [
{"name":"Cisco-IOS-XE-interfaces-oper:interfaces"},
{"name":"interface"},
{"name":"GigabitEthernet0/0"},
{"name":"oper-status"}
]
},
"val": "down"
}
]
}
]
}
}
{
"update": {
"prefix": {},
"notification": [
{
"timestamp": 1710000030000000,
"update": [
{
"path": {
"elem": [
{"name":"Cisco-IOS-XE-interfaces-oper:interfaces"},
{"name":"interface"},
{"name":"GigabitEthernet0/0"},
{"name":"counters"}
]
},
"val": {
"in-octets": 123567890,
"out-octets": 98777777,
"in-errors": 10,
"out-errors": 0
}
}
]
}
]
}
}
Explanation: The device can emit both event-style updates and periodic samples in the same stream; each notification is tagged with a timestamp for correlation.
Verification Checklist
- Check 1: Confirm device reports SUBSCRIBE capability — send Capabilities RPC and verify "SUBSCRIBE" is listed in supported_rpcs.
- How: Send Capabilities JSON and inspect response for "SUBSCRIBE".
- Check 2: ON_CHANGE events arrive when operational state changes.
- How: Bring an interface down/up on the device and verify a notification containing oper-status change and that a subsequent sync_response was previously received.
- Check 3: SAMPLE notifications arrive at approximately the configured interval (30s).
- How: Start SAMPLE subscription and observe periodic notifications with interface counters at ~30 second intervals.
Common Mistakes
| Symptom | Cause | Fix |
|---|---|---|
| No response to Capabilities RPC | Collector used unsupported encoding or transport, or gRPC connection failed | Ensure gRPC connection established, use JSON_IETF if device lists it; check network and TLS/mTLS settings between collector and device |
| No ON_CHANGE notifications after an interface change | Subscription never received sync_response (stream not established) or wrong YANG path | Verify initial SUBSCRIBE returned capabilities and sync_response; confirm subscription path matches device YANG module (e.g., Cisco-IOS-XE-interfaces-oper:interfaces/interface) |
| SAMPLE notifications appear at wrong frequency | sampleInterval misconfigured or misinterpreted units | sampleInterval is in nanoseconds; 30000000000 = 30 seconds. Adjust value accordingly |
| Large telemetry payloads or high CPU | Subscribing too many paths or too-frequent sampling | Reduce subscription scope, aggregate paths, or increase sample interval; consider PROTO encoding for efficiency |
Key Takeaways
- gNMI SUBSCRIBE supports both ON_CHANGE (event-driven) and SAMPLE (periodic) modes; choose ON_CHANGE for events and SAMPLE for metrics.
- Always query device capabilities first to confirm supported encodings, models, and RPCs (GET/SET/SUBSCRIBE).
- The device sends a sync_response after the initial dataset is transmitted — collectors should use this as the baseline marker before processing deltas.
- Use JSON_IETF for human readable telemetry during testing; PROTO encoding gives improved efficiency in high-scale production environments.
Warning: In production, secure your gNMI transport (TLS/mTLS) and validate device certificates. Telemetry channels may carry sensitive operational data.
This completes Lesson 3: gNMI Subscriptions and Streaming. In the next lesson we will cover certificate authentication for gNMI and setting up secure gRPC channels to the collector.