Notifications¶
pyNetX supports NETCONF notification subscriptions through subscribe_async().
Notifications are received on a separate SSH/NETCONF session from the primary
RPC session.
Basic subscription¶
import asyncio
import pyNetX
async def main():
client = pyNetX.NetconfClient(
hostname="192.168.1.1",
username="admin",
password="admin",
notif_queue_size=1000,
label="leaf-01",
)
await client.connect_async()
try:
reply = await client.subscribe_async(stream="NETCONF")
print("subscription reply:", reply)
while client.is_subscription_active():
notification = await client.next_notification_async(timeout_ms=1000)
if notification:
print(notification)
finally:
try:
client.delete_subscription()
finally:
await client.disconnect_async()
asyncio.run(main())
Queue helpers¶
API |
Description |
|---|---|
|
Synchronous queue read. Releases the Python GIL while waiting. |
|
Awaitable queue read. |
|
Inspect queued notifications without consuming them. Use |
|
Return the current queue depth. |
|
Return whether the subscription is active. |
|
Close the notification session/subscription. Safe to call during cleanup. |
Bounded and unbounded queues¶
notif_queue_size=-1 creates an unbounded notification queue. This is the
default.
A non-negative notif_queue_size creates a bounded queue. If the queue is
full, pyNetX drops incoming notifications and emits health events such as
notification_queue_full and notification_drops_summary.
Example bounded queue:
client = pyNetX.NetconfClient(
hostname="192.168.1.1",
username="admin",
password="admin",
notif_queue_size=1000,
notif_drop_event_threshold=10,
label="leaf-01",
)
Notification stream parser behavior¶
pyNetX v2.0.7 treats the notification SSH channel as a byte stream. A single reactor callback may receive multiple complete notifications, one complete notification plus part of the next one, only part of a notification, or malformed device data. The client keeps a persistent receive buffer for the active subscription and parses from that buffer.
Device stream case |
Behavior |
Health event |
|---|---|---|
One complete notification ending in |
Queued with the EOM marker. |
None |
Multiple complete notifications in one read |
Split and queued as separate notifications. |
None |
Complete notification followed by partial next notification |
Complete notification is queued; trailing partial bytes stay in the receive buffer. |
None immediately |
Partial notification completed by a later read |
Saved partial bytes are combined with later bytes and queued as one complete notification. |
None |
New |
Previous fragment is queued as an abandoned partial and parsing continues from the new start tag. |
|
EOM-delimited data is not valid notification XML |
Malformed frame is queued for inspection. |
|
Empty EOM-only frame |
Empty frame is dropped. |
|
Orphan bytes before a notification start tag |
Orphan prefix is dropped and parsing continues. |
|
Complete notification XML is followed by another notification without an EOM between them |
First notification is recovered and queued without adding a synthetic EOM. |
|
Partial bytes never receive EOM before a guard fires |
Partial bytes are queued without EOM. |
|
For backward compatibility, valid EOM-delimited notifications returned by
next_notification() and next_notification_async() still include the
]]>]]> marker. Partial and recovered missing-EOM fragments are returned as
received, without adding a synthetic marker.
Incomplete notification guards¶
Some devices or broken test targets may send partial notification XML without
the NETCONF ]]>]]> end marker. pyNetX has two guards:
notif_incomplete_max_kb: maximum partial notification size before a guard fires.notif_incomplete_timeout: maximum seconds to wait for EOM after partial data starts.
At least one guard must remain enabled. Do not set both to -1.
When a guard fires, pyNetX emits an incomplete_notification health event and queues the partial bytes for inspection.