Hands-on OSPF operations

OSPF Watcher bootcamp runbook

Make one controlled change at a time, predict its effect, then verify the same fact from the Watcher event through Topolograph.

Start the runbook

Useful links: OSPF Watcher - repository and deploy guide · Topolograph

1. How this runbook works

For each exercise: make one change, predict the OSPF result, observe the Watcher event, then find the same fact in Topolograph Monitoring, in the SDK, and - for the correlation exercises - in the agent's answer. Restore the lab before the next independent exercise.

  1. The Watcher CSV line is the deterministic source event.
  2. Monitoring and the SDK prove the event was ingested and is queryable.
  3. The agent is used only to correlate several events into one incident, never as a replacement for the source line.

2. Bringing up the six-router lab

Run the public GRE-based ospf01 topology from the OSPF Watcher repository.

Command

cd containerlab/ospf01
sudo clab inspect --all
sudo clab destroy --topo ospf01.clab.yml --cleanup   # only if a stale ospf01 is listed
sudo ./prepare.sh
sudo clab deploy --topo ospf01.clab.yml
sudo docker logs clab-ospf01-ospf-watcher
sudo tail -f watcher/logs/watcher1.ospf.log
Required observations Facts to confirm before continuing
  • Six router containers plus the watcher are up: docker ps --filter name=clab-ospf01 lists clab-ospf01-router1..6 and clab-ospf01-ospf-watcher, all State=Up.
  • The watcher has the LSDB and is sniffing: docker logs clab-ospf01-ospf-watcher shows an "OSPF LSDB received" line and "Start sniffing on interface: eth1".
  • Adjacencies are Full: docker exec clab-ospf01-router1 vtysh -c 'show ip ospf neighbor' lists each neighbour in Full state; the first Topolograph graph then contains six routers.

Rollback: When you finish the runbook, remove the lab with sudo clab destroy --topo ospf01.clab.yml --cleanup from containerlab/ospf01/.

3. Network event record format

The runbook uses three event families: host, metric, and network. Read event_object as the changed object, event_status as the transition, and event_detected_by as the advertising or detecting router. graph_time is the watcher's own label for the run and selects the Topolograph graph.

Read the metric line above as one sentence: at 2026-09-06T13:11:04Z the watcher ospfwatcher-demo saw router 10.10.10.2 re-advertise its link toward 10.10.10.3 with the cost changed from 10 to 222, on local interface 192.168.23.1, in area 0.0.0.0 / AS 12345. The network line right after it carries the same 10 -> 222 move for the subnet 192.168.23.0/24 on that link.

  1. Fields in order: watcher_time, watcher_name, event_name, event_object, event_status, [cost fields], event_detected_by, graph_time, area_num, asn, [type fields], sesid, srcid.
  2. area_num 0.0.0.0 and asn 12345 identify the routing domain; sesid is the watcher session, srcid the source router id.

Watcher CSV

host: 2026-09-06T13:04:25.256Z,ospfwatcher-demo,host,10.10.10.2,down,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
metric: 2026-09-06T13:11:04.205Z,ospfwatcher-demo,metric,10.10.10.3,changed,old_cost:10,new_cost:222,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.23.1,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
network: 2026-09-06T13:11:04.207Z,ospfwatcher-demo,network,192.168.23.0/24,changed,old_cost:10,new_cost:222,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,internal,0,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1

4. Where events come from

One OSPF change produces one Watcher CSV line. Fluent Bit forwards it to Topolograph, which stores it and exposes it through the Monitoring page, the event API, and the SDK. Each exercise below checks the same fact at every level available to you.

  1. ospf01 Watcher log -> Fluent Bit CSV parser -> Topolograph ingestion -> Monitoring page + event API -> Topolograph SDK
  2. Monitoring page: OSPF/IS-IS Real-Time Monitoring. Choose the graph by its timestamp, set a time window, click Find logs; toggles filter subnet / link / metric events.
  3. The graph selector on that page lists every topology snapshot the watcher reported, newest first - that is the topology the watcher sent.
Topolograph OSPF/IS-IS Real-Time Monitoring page for graph 06Sep2026_12h31m41s: graph selector and Find logs, the event feed showing the 10 -> 222 and 222 -> 10 metric and 192.168.23.0/24 chips, and the topology graph below with the six routers and link weights.

SDK request

from topolograph import Topolograph

topo = Topolograph(url="http://<your-topolograph>:8080",
                   username="<email>", password="<password>")
graph = topo.graphs.get(latest=True)
print(graph.graph_time, graph.protocol, graph.hosts)

SDK output

06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo ospf {'count': 6}

5. Cost change on a point-to-point link

Change router2 eth1 toward router3. OSPF costs are directed, so the reverse router3-to-router2 cost must not be described as the same scalar.

Command

sudo docker exec clab-ospf01-router2 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'ip ospf cost 222'

Verify the change. Open each source available in your environment and check it against the examples below.

Watcher log The metric and network lines for the change and its rollback 4 lines

Watcher CSV

2026-09-06T13:11:04.205Z,ospfwatcher-demo,metric,10.10.10.3,changed,old_cost:10,new_cost:222,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.23.1,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:11:04.207Z,ospfwatcher-demo,network,192.168.23.0/24,changed,old_cost:10,new_cost:222,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,internal,0,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
# rollback (no ip ospf cost 222):
2026-09-06T13:11:18.365Z,ospfwatcher-demo,metric,10.10.10.3,changed,old_cost:222,new_cost:10,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.23.1,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:11:18.367Z,ospfwatcher-demo,network,192.168.23.0/24,changed,old_cost:222,new_cost:10,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,internal,0,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
Monitoring The 10 -> 222 metric change on the event feed

On OSPF/IS-IS Real-Time Monitoring, pick this graph in Choose the graph, set the From/To window around 13:11 UTC and click Find logs. With Changed metric on, the feed shows one row: object 10.10.10.3, detected by 10.10.10.2, 10 -> 222 - the router2 -> router3 direction only. The reverse direction is not listed. The topology graph below the feed redraws the router2-router3 link with the new weight.

SDK verification get_adjacency_events and get_network_events return the same object and costs 2 events

SDK request

adj = graph.events.get_adjacency_events(
    start_time="2026-09-06T13:11:00Z", end_time="2026-09-06T13:11:10Z")
net = graph.events.get_network_events(
    start_time="2026-09-06T13:11:00Z", end_time="2026-09-06T13:11:10Z")
print(adj["adjacency_cost_change_events"])
print(net["network_cost_change_events"])
e = adj["adjacency_cost_change_events"][0]
print(e.event_object, e.event_detected_by, e.old_cost, e.new_cost, e.local_ip_address)

SDK output

[Event(name=metric, status=changed, object=10.10.10.3)]
[Event(name=network, status=changed, object=192.168.23.0/24)]
10.10.10.3 10.10.10.2 10 222 192.168.23.1

The metric event names the router2 -> router3 direction (event_object 10.10.10.3, event_detected_by 10.10.10.2), cost 10 -> 222; 192.168.23.0/24 carries the same move. The reverse direction is absent from both lists.

Ask the agent A generic incident question about router2

Prompt: What happened with router2 in the last 10 minutes?

Captured answer (Qwen; wording varies between runs)

In the 06Sep2026_12h31m41s snapshot, router2 (10.10.10.2) had one change in this window - an OSPF metric change on its link to 10.10.10.3, detected by router2 itself. No prefix, link, or node went down.

13:11:04 - router2 raised the cost of its link to 10.10.10.3 from 10 to 222 (192.168.23.0/24, area 0).

13:11:19 - router2 restored that cost from 222 back to 10.

Only the router2 -> 10.10.10.3 direction produced an event; the reverse 10.10.10.3 -> router2 direction did not change.

Observed on this run: router2 re-advertised its link to router3 at cost 222 (was 10); the reverse router3 -> router2 direction did not change.

Rollback: Run no ip ospf cost 222 and confirm the inverse 222 -> 10 metric and network events.

6. Detecting internal and external prefix events

Run each prefix experiment independently. An OSPF loopback configured with a /24 mask is advertised as a /32 host route by this lab.

  1. 6a. Add 192.168.123.1/24 to router2 loopback; observe 192.168.123.1/32 up and cost -1 -> 0 (internal).
  2. 6b. Add 10.10.136.6/24 to router6 loopback; observe 10.10.136.6/32 up and cost -1 -> 0 (internal).
  3. 6c. Remove static route 6.6.6.6/32 on router6; observe 6.6.6.6/32 down and cost 11 -> -1 (external, type 2).

Command

sudo docker exec clab-ospf01-router2 vtysh \
  -c 'conf t' -c 'interface lo' -c 'ip address 192.168.123.1/24'

Verify the change. Open each source available in your environment and check it against the examples below.

Watcher log One up/down and one changed line per sub-exercise 6 lines

Watcher CSV

# 6a router2: ip address 192.168.123.1/24 on interface lo
2026-09-06T13:12:06.600Z,ospfwatcher-demo,network,192.168.123.1/32,up,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:12:06.601Z,ospfwatcher-demo,network,192.168.123.1/32,changed,old_cost:-1,new_cost:0,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,internal,0,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
# 6b router6: ip address 10.10.136.6/24 on interface lo
2026-09-06T13:12:28.916Z,ospfwatcher-demo,network,10.10.136.6/32,up,10.10.10.6,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:12:28.917Z,ospfwatcher-demo,network,10.10.136.6/32,changed,old_cost:-1,new_cost:0,10.10.10.6,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,internal,0,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
# 6c router6: no ip route 6.6.6.6/32 192.168.36.3
2026-09-06T13:12:51.315Z,ospfwatcher-demo,network,6.6.6.6/32,down,10.10.10.6,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:12:51.315Z,ospfwatcher-demo,network,6.6.6.6/32,changed,old_cost:11,new_cost:-1,10.10.10.6,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,external,2,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
Monitoring The new and withdrawn prefixes on the feed

On OSPF/IS-IS Real-Time Monitoring, pick this graph, set the From/To window around 13:12 UTC and click Find logs. With New/Old Subnets on, 192.168.123.1/32 and 10.10.136.6/32 appear as added and 6.6.6.6/32 as withdrawn; the topology graph below the feed adds the two host routes and drops the withdrawn one.

SDK verification get_network_events, one changed event per sub-exercise 3 events

SDK request

for window in [("2026-09-06T13:12:05Z", "2026-09-06T13:12:08Z"),
               ("2026-09-06T13:12:27Z", "2026-09-06T13:12:30Z"),
               ("2026-09-06T13:12:50Z", "2026-09-06T13:12:53Z")]:
    net = graph.events.get_network_events(start_time=window[0], end_time=window[1])
    for e in net["network_up_down_events"]:
        print(e.event_object, e.event_status, e.event_detected_by,
              f"{e.old_cost} -> {e.new_cost}", e.subnet_type, e.int_ext_subtype)

SDK output

192.168.123.1/32 changed 10.10.10.2 -1 -> 0 internal 0
10.10.136.6/32 changed 10.10.10.6 -1 -> 0 internal 0
6.6.6.6/32 changed 10.10.10.6 11 -> -1 external 2

A /24 on the loopback is advertised as a /32. Availability is an up/down event; the cost move is a changed event. The withdrawn route is external / type 2, the two loopbacks internal / 0.

Ask the agent A generic question about prefix changes

Prompt: Which prefixes changed in the last 15 minutes, and were they internal or external?

Captured answer (Qwen; wording varies between runs)

In the 06Sep2026_12h31m41s snapshot, three prefixes changed between 13:12 and 13:13 UTC - each advertised and then withdrawn within about ten seconds.

192.168.123.1/32 - internal, seen from 10.10.10.2 at 13:12:07 (cost 0), gone by 13:12:18.

10.10.136.6/32 - internal, seen from 10.10.10.6 at 13:12:29 (cost 0), gone by 13:12:40.

6.6.6.6/32 - external, withdrawn by 10.10.10.6 at 13:12:51 (was cost 11), back by 13:13:03.

192.168.123.1/32 and 10.10.136.6/32 are internal; 6.6.6.6/32 is the only external prefix.

Observed on this run: 192.168.123.1/32 and 10.10.136.6/32 were added as internal host routes; 6.6.6.6/32 (external, type 2) was withdrawn.

Rollback: 6a: no ip address 192.168.123.1/24 on router2 interface lo. 6b: no ip address 10.10.136.6/24 on router6 interface lo. 6c: ip route 6.6.6.6/32 192.168.36.3 on router6. Confirm the inverse event after each.

7. Detecting a connectivity loss and its recovery

Shut router2 eth1, inspect the correlated event set, then restore the interface with no shutdown.

Command

# down
sudo docker exec clab-ospf01-router2 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'shutdown'
# recovery
sudo docker exec clab-ospf01-router2 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'no shutdown'

Verify the change. Open each source available in your environment and check it against the examples below.

Watcher log The down set and the recovery set for the router2-router3 link down + up

Watcher CSV

# router2: interface eth1 / shutdown
2026-09-06T13:04:25.256Z,ospfwatcher-demo,host,10.10.10.2,down,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:04:25.256Z,ospfwatcher-demo,metric,10.10.10.2,changed,old_cost:10,new_cost:-1,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.23.2,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:04:25.257Z,ospfwatcher-demo,metric,10.10.10.3,changed,old_cost:10,new_cost:-1,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.23.1,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:04:25.259Z,ospfwatcher-demo,network,192.168.23.0/24,down,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
# router2: interface eth1 / no shutdown
2026-09-06T13:04:40.438Z,ospfwatcher-demo,network,192.168.23.0/24,up,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:04:40.458Z,ospfwatcher-demo,host,10.10.10.2,up,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:04:40.459Z,ospfwatcher-demo,metric,10.10.10.2,changed,old_cost:-1,new_cost:10,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.23.2,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:04:45.557Z,ospfwatcher-demo,host,10.10.10.3,up,10.10.10.2,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
Monitoring The outage as one wave on the feed

On OSPF/IS-IS Real-Time Monitoring, pick this graph, set the From/To window around 13:04 UTC and click Find logs. With Up/Down Links on, 10.10.10.2 shows down then up, both directed metrics move to -1 and back, and 192.168.23.0/24 goes unreachable and returns; the topology graph below the feed drops the router2-router3 link and restores it.

SDK verification get_adjacency_events returns the paired down/up moves 4 moves

SDK request

adj = graph.events.get_adjacency_events(
    start_time="2026-09-06T13:04:20Z", end_time="2026-09-06T13:04:50Z")
for e in adj["all_host_up_down_events"]:
    print(e.event_name, e.event_object, "detected_by", e.event_detected_by,
          f"{e.old_cost} -> {e.new_cost}", e.watcher_time[11:19])

SDK output

metric 10.10.10.2 detected_by 10.10.10.3 10 -> -1 13:04:25
metric 10.10.10.3 detected_by 10.10.10.2 10 -> -1 13:04:25
metric 10.10.10.2 detected_by 10.10.10.3 -1 -> 10 13:04:40
metric 10.10.10.3 detected_by 10.10.10.2 -1 -> 10 13:04:45

host 10.10.10.2 down detected by 10.10.10.3, both directed metrics to -1, 192.168.23.0/24 down; then the mirror recovery.

Ask the agent A generic incident question about the network

Prompt: What happened in the network in the last 30 minutes?

Captured answer (Qwen; wording varies between runs)

There was 1 wave of activity, starting at 13:04:25 UTC and involving 2 devices - a brief link flap between 10.10.10.2 and 10.10.10.3 that fully recovered (converged) within ~21 seconds.

13:04:25 - device 10.10.10.2 lost adjacency (link down, cost 10 -> -1).

13:04:25 - device 10.10.10.3 lost adjacency (link down, cost 10 -> -1).

13:04:41 - device 10.10.10.2 restored adjacency (link up, cost -1 -> 10).

13:04:46 - device 10.10.10.3 restored adjacency (link up, cost -1 -> 10).

Pattern: flap - all devices recovered within the wave, so the network re-converged.

The answer names the failed adjacency (10.10.10.2 - 10.10.10.3) detected by 10.10.10.3, both metrics at -1, and the recovery - the same facts as the CSV lines above, from a question that never says 'adjacency' or 'failure'.

Rollback: no shutdown restores the interface; wait for the host, network and metric recovery events.

8. Reporting on a broadcast transit segment

Use router6 eth1 to compare transit-segment reporting with the point-to-point case.

Command

# cost
sudo docker exec clab-ospf01-router6 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'ip ospf cost 66'
# then, separately: shutdown, then no shutdown
sudo docker exec clab-ospf01-router6 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'shutdown'
sudo docker exec clab-ospf01-router6 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'no shutdown'

Verify the change. Open each source available in your environment and check it against the examples below.

Watcher log The cost change names the far router id, and the shutdown/recovery pair cost + up/down

Watcher CSV

# router6: interface eth1 / ip ospf cost 66   (and rollback ip ospf cost 6)
2026-09-06T13:13:13.841Z,ospfwatcher-demo,metric,10.10.10.3,changed,old_cost:6,new_cost:66,10.10.10.6,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.36.6,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:13:25.028Z,ospfwatcher-demo,metric,10.10.10.3,changed,old_cost:66,new_cost:6,10.10.10.6,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.36.6,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
# router6: interface eth1 / shutdown
2026-09-06T13:13:36.241Z,ospfwatcher-demo,host,10.10.10.6,down,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:13:36.241Z,ospfwatcher-demo,metric,10.10.10.6,changed,old_cost:10,new_cost:-1,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.36.3,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
# router6: interface eth1 / no shutdown
2026-09-06T13:14:30.447Z,ospfwatcher-demo,host,10.10.10.6,up,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
2026-09-06T13:14:30.447Z,ospfwatcher-demo,metric,10.10.10.6,changed,old_cost:-1,new_cost:10,10.10.10.3,06Sep2026_12h31m41s_6_hosts_bgpls_ospfwatcher-demo,0.0.0.0,12345,192.168.36.3,,e9a31bb2-a9ee-11f1-85b5-1e1be0e3c37d,10.10.10.1
Monitoring The transit cost change and the router6 outage

On OSPF/IS-IS Real-Time Monitoring, pick this graph, set the From/To window around 13:13-13:14 UTC and click Find logs. The metric event object is 10.10.10.3 (the far router id) with event_detected_by 10.10.10.6 - unlike a point-to-point link, where the object is the neighbour. The topology graph below the feed reweights, then drops and restores the router6 attachment.

SDK verification get_adjacency_events for the transit cost and the shutdown/recovery 4 moves

SDK request

cost = graph.events.get_adjacency_events(
    start_time="2026-09-06T13:13:10Z", end_time="2026-09-06T13:13:30Z")
for e in cost["adjacency_cost_change_events"]:
    print("cost", e.event_object, e.event_detected_by, f"{e.old_cost} -> {e.new_cost}", e.watcher_time[11:19])
flap = graph.events.get_adjacency_events(
    start_time="2026-09-06T13:13:30Z", end_time="2026-09-06T13:14:35Z")
for e in flap["all_host_up_down_events"]:
    print("updown", e.event_object, e.event_detected_by, f"{e.old_cost} -> {e.new_cost}", e.watcher_time[11:19])

SDK output

cost 10.10.10.3 10.10.10.6 6 -> 66 13:13:13
cost 10.10.10.3 10.10.10.6 66 -> 6 13:13:25
updown 10.10.10.6 10.10.10.3 10 -> -1 13:13:36
updown 10.10.10.6 10.10.10.3 -1 -> 10 13:14:30

On a transit segment the metric event names the far router id (10.10.10.3), detected by router6; the shutdown is host 10.10.10.6 down, then recovery.

Ask the agent A generic incident question about router6

Prompt: What happened on router6 in the last 20 minutes?

Captured answer (Qwen; wording varies between runs)

In the 06Sep2026_12h31m41s snapshot, router6 (10.10.10.6) first flapped the cost of its link to 10.10.10.3, then lost that adjacency and recovered - about a 55 second outage.

13:13:14 - router6 changed the cost of its link to 10.10.10.3 from 6 to 66.

13:13:25 - router6 restored that cost from 66 back to 6.

13:13:36 - neighbor 10.10.10.3 reported its adjacency to router6 down (metric 10 -> -1).

13:14:31 - 10.10.10.3 reported the adjacency back up (metric -1 -> 10); router6 re-advertised its side at cost 6 by 13:14:36.

Pattern: a brief metric flap on the router6 - 10.10.10.3 link, then a short outage that fully recovered.

Observed on this run: the router6 -> router3 transit cost moved 6 -> 66 and back, then the router6 attachment went down (metric -1) and recovered.

Rollback: ip ospf cost 6 restores the baseline; no shutdown restores the adjacency. Confirm the inverse metric events.

Topolograph 2.69.4 📣 Join to the community!