Operacion practica de IS-IS

Manual practico de IS-IS Watcher

Haz un solo cambio controlado cada vez, predice su efecto y verifica el mismo hecho desde el evento del Watcher hasta Topolograph.

Iniciar el manual

Enlaces utiles: IS-IS Watcher - repositorio y guia de despliegue · Topolograph

1. Cómo funciona este manual

Para cada ejercicio: haz un cambio, predice el resultado IS-IS, observa el evento del Watcher y luego encuentra el mismo hecho en Topolograph Monitoring, en el SDK y - en el ejercicio de correlacion - en la respuesta del agente. Restaura el laboratorio antes del siguiente ejercicio independiente.

isis01 es un dominio FRR de seis routers en un area (49.0001, AS 65100). router2-router3 usa Level-1-2; router3 hacia router6 y el tramo de router3 hacia la LAN router4/router5 son solo Level-2. El Watcher usa una adyacencia Level-2 en router1 y resuelve cada System ID a su hostname mediante el TLV de hostname dinamico de IS-IS, por lo que los eventos nombran router2, router3, router6 en vez de 0100.1001.000x.

  1. La linea CSV del Watcher es el evento de origen determinista.
  2. Monitoring y el SDK demuestran que el evento se ingirio y se puede consultar.
  3. El agente se usa solo para correlacionar varios eventos en un incidente, nunca como sustituto de la linea de origen.

2. Puesta en marcha y validación del laboratorio de seis routers

Ejecuta la topologia publica isis01 basada en GRE del repositorio de IS-IS Watcher. prepare.sh tambien crea el puente isis-br-dr y carga los modulos de kernel MPLS que IS-IS TE necesita.

Primero comprueba si ya hay un laboratorio en marcha con sudo clab inspect --all. Si aparece un isis01 obsoleto, desmontalo con sudo clab destroy --topo isis01.clab.yml --cleanup desde containerlab/isis01/ antes de volver a desplegar; nombra el archivo de topologia para no tocar ningun otro laboratorio.

Comando

cd containerlab/isis01
sudo clab inspect --all
sudo clab destroy --topo isis01.clab.yml --cleanup   # only if a stale isis01 is listed
sudo ./prepare.sh
sudo clab deploy --topo isis01.clab.yml
sudo docker logs clab-isis01-isis-watcher
sudo tail -f watcher/logs/watcher1.isis.log
Observaciones requeridas Hechos a confirmar antes de continuar
  • Seis contenedores de router mas el watcher estan activos: docker ps --filter name=clab-isis01 lista clab-isis01-router1..6 y clab-isis01-isis-watcher, todos con State=Up.
  • El watcher tiene la LSDB y esta capturando: docker logs clab-isis01-isis-watcher muestra ISIS LSDB has been received y Sniffing packets on interface: eth1.
  • Las adyacencias estan Up: docker exec clab-isis01-router1 vtysh -c 'show isis neighbor' lista router3 en estado Up; el primer grafo de Topolograph contiene entonces seis routers.

Reversion: Cuando termines el manual, elimina el laboratorio con sudo clab destroy --topo isis01.clab.yml --cleanup desde containerlab/isis01/.

3. Formato del registro de evento de red

El manual usa tres familias de eventos: host, metric y network. Lee event_object como el objeto modificado, event_status como la transicion y event_detected_by como el router que anuncia o detecta. graph_time es la etiqueta propia del watcher para la ejecucion y selecciona el grafo de Topolograph.

Las lineas IS-IS llevan un campo level (1 o 2) que las lineas OSPF no tienen: es el tercer campo, justo despues de watcher_name. Lee la linea metric de arriba como una frase: a las 2026-09-07T06:55:14Z el watcher lab-isis01 vio a router3 reanunciar su enlace Level-1 hacia router2 con la metrica cambiada de 10 a -1 (adyacencia perdida), en la interfaz con direccion 192.168.23.2 en el area 49.0001 / AS 65100. La identidad detras de router2 es su NET / System ID 49.0001.0100.1001.0002.00; el watcher imprime el hostname porque cada router anuncia el TLV de hostname dinamico.

Una linea host o network up/down simple no lleva campos de coste, asi que Fluent Bit reenvia solo la linea changed emparejada. Topolograph deduce que una adyacencia cae por la linea metric cuyo new_cost es -1, y que vuelve por aquella cuyo old_cost es -1.

  1. Campos en orden: watcher_time, watcher_name, level, event_name, event_object, event_status, [campos de coste], event_detected_by, graph_time, area_num, asn, [local_ip, remote_ip | subnet_type, int_ext_subtype], sesid, srcid.
  2. area_num 49.0001 y asn 65100 identifican el dominio de enrutamiento; sesid es la sesion del watcher, srcid el System ID del router de origen (router1, 0100.1001.0001).

CSV de Watcher

host:    2026-09-07T06:55:14.754Z,lab-isis01,1,host,router2,down,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,192.168.23.2,192.168.23.1,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
metric:  2026-09-07T06:55:14.756Z,lab-isis01,1,metric,router2,changed,old_cost:10,new_cost:-1,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,192.168.23.2,192.168.23.1,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
network: 2026-09-07T06:55:14.761Z,lab-isis01,1,network,192.168.23.0/24,changed,old_cost:10,new_cost:-1,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,internal,0,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001

4. De dónde vienen los eventos

Un cambio IS-IS produce una linea CSV del Watcher por nivel. Fluent Bit la reenvia a Topolograph, que la almacena y la expone en la pagina de Monitoring, la API de eventos y el SDK. Cada ejercicio de abajo comprueba el mismo hecho en cada nivel disponible para ti.

  1. Registro del Watcher isis01 -> parser CSV de Fluent Bit -> ingesta de Topolograph -> pagina de Monitoring + API de eventos -> SDK de Topolograph
  2. Pagina de Monitoring: OSPF/IS-IS Real-Time Monitoring. Elige el grafo por su marca de tiempo en Choose the graph, define la ventana From/To en UTC, activa los interruptores L1 y L2, pulsa Find logs; los interruptores New/Old Subnets, Up/Down Links y Changed metric filtran lo que se lista.
  3. El selector de grafo lista cada instantánea de topología que el watcher reportó: esa es la topología que el watcher envió.
Controles de Topolograph OSPF/IS-IS Real-Time Monitoring para el grafo 07Sep2026_06h49m43s_6_hosts: el selector de grafo, la ventana de tiempo From/To, los interruptores de nivel L1 y L2 ambos activados, los interruptores New/Old Subnets, Up/Down Links y Changed metric, y Find logs. El panel Watchers Status muestra "No watchers registered yet" porque el watcher de containerlab envia topologia pero no heartbeats.

Peticion del SDK

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)

Salida del SDK

07Sep2026_06h49m43s_6_hosts isis {'count': 6}

5. Cambio de coste en un enlace punto a punto

Cambia router2 eth1 hacia router3. Las metricas IS-IS son dirigidas, asi que la metrica inversa de router3 a router2 no debe describirse como el mismo escalar. router2 eth1 no tiene un isis metric explicito, asi que parte del valor por defecto de wide-metric, 10. router2-router3 es un circuito Level-1-2, asi que el cambio se anuncia en ambos niveles.

Comando

sudo docker exec clab-isis01-router2 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'isis metric 222'

Verifica el cambio. Abre cada fuente disponible en tu entorno y contrástala con los ejemplos de abajo.

Registro del Watcher Las lineas metric y network del cambio y su reversion, en L1 y L2 L1 + L2

CSV de Watcher

# router2: interface eth1 / isis metric 222
2026-09-07T06:52:15.035Z,lab-isis01,1,metric,router3,changed,old_cost:10,new_cost:222,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:52:15.037Z,lab-isis01,1,network,192.168.23.0/24,changed,old_cost:10,new_cost:222,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,internal,0,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:52:15.045Z,lab-isis01,2,metric,router3,changed,old_cost:10,new_cost:222,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
# rollback (isis metric 10):
2026-09-07T06:52:37.514Z,lab-isis01,1,metric,router3,changed,old_cost:222,new_cost:10,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:52:37.526Z,lab-isis01,2,metric,router3,changed,old_cost:222,new_cost:10,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
Monitoreo El cambio de metrica 10 -> 222 en el flujo de eventos

En OSPF/IS-IS Real-Time Monitoring, elige 07Sep2026_06h49m43s_6_hosts en Choose the graph, define la ventana From/To en torno a las 06:52 UTC, activa L1 y L2 y pulsa Find logs. Con Changed metric activado, el flujo muestra object router3, detected by router2, 10 -> 222 - solo la direccion router2 -> router3, una vez para L1 y otra para L2. La direccion inversa no aparece.

Verificacion con SDK get_adjacency_events y get_network_events devuelven el mismo objeto y los mismos costes 2 eventos

Peticion del SDK

adj = graph.events.get_adjacency_events(
    start_time="2026-09-07T06:52:14Z", end_time="2026-09-07T06:52:20Z")
net = graph.events.get_network_events(
    start_time="2026-09-07T06:52:14Z", end_time="2026-09-07T06:52:20Z")
for e in adj["adjacency_cost_change_events"]:
    print(e.event_object, e.event_detected_by, e.old_cost, "->", e.new_cost, "L" + str(e.level_number))
for e in net["network_cost_change_events"]:
    print(e.event_object, e.old_cost, "->", e.new_cost, "L" + str(e.level_number))

Salida del SDK

router3 router2 10 -> 222 L1
router3 router2 10 -> 222 L2
3ffe::192:168:23:2/127 10 -> 222 L1
192.168.23.0/24 10 -> 222 L1
3ffe::192:168:23:2/127 10 -> 222 L2
192.168.23.0/24 10 -> 222 L2

El evento metric nombra la direccion router2 -> router3 (event_object router3, event_detected_by router2), 10 -> 222, en ambos niveles; las subredes conectadas IPv4 e IPv6 llevan el mismo cambio. La direccion inversa no aparece.

Pregunta al agente Una pregunta generica de incidente sobre router2

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

Una respuesta valida nombra la direccion router2 -> router3 y ambos valores de metrica (10 y 222) sin que la pregunta mencione coste o metrica, y no afirma que la direccion inversa router3 -> router2 cambio.

Reversion: Ejecuta isis metric 10 (o no isis metric) en router2 eth1 y confirma los eventos inversos 222 -> 10 metric y network.

6. Detección de eventos de prefijos internos y externos

Ejecuta cada experimento de prefijo por separado. A diferencia de OSPF, IS-IS anuncia un loopback con la mascara con que esta configurado y lo cuesta con la metrica de la interfaz: un /24 sigue siendo /24 con coste 10, no se colapsa a una ruta host /32. router6 es solo Level-2, asi que sus prefijos aparecen solo en L2.

  1. 6a. En router2, interface lo / ip address 192.168.123.1/24; observa 192.168.123.0/24 up y coste -1 -> 10 en L1 y L2.
  2. 6b. En router6, interface lo / ip address 10.10.36.6/24; observa 10.10.36.0/24 up y coste -1 -> 10 en L2.
  3. 6c. En router6, no ip route 6.6.6.6/32 192.168.36.3; observa 6.6.6.6/32 down y coste 11 -> -1 en L2. FRR la redistribuye en IS-IS sin el bit external, asi que el watcher la marca como internal.

Comando

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

Verifica el cambio. Abre cada fuente disponible en tu entorno y contrástala con los ejemplos de abajo.

Registro del Watcher Una linea up/down y una changed por sub-ejercicio 3 prefijos

CSV de Watcher

# 6a router2: interface lo / ip address 192.168.123.1/24
2026-09-07T06:52:52.074Z,lab-isis01,1,network,192.168.123.0/24,up,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:52:52.075Z,lab-isis01,1,network,192.168.123.0/24,changed,old_cost:-1,new_cost:10,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,internal,0,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
# 6b router6: interface lo / ip address 10.10.36.6/24
2026-09-07T06:53:39.099Z,lab-isis01,2,network,10.10.36.0/24,up,router6,07Sep2026_06h49m43s_6_hosts,49.0001,65100,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:53:39.099Z,lab-isis01,2,network,10.10.36.0/24,changed,old_cost:-1,new_cost:10,router6,07Sep2026_06h49m43s_6_hosts,49.0001,65100,internal,0,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
# 6c router6: no ip route 6.6.6.6/32 192.168.36.3
2026-09-07T06:54:14.533Z,lab-isis01,2,network,6.6.6.6/32,down,router6,07Sep2026_06h49m43s_6_hosts,49.0001,65100,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:54:14.533Z,lab-isis01,2,network,6.6.6.6/32,changed,old_cost:11,new_cost:-1,router6,07Sep2026_06h49m43s_6_hosts,49.0001,65100,internal,0,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
Monitoreo Los prefijos nuevos y retirados en el flujo

En OSPF/IS-IS Real-Time Monitoring, elige este grafo, define la ventana en torno a 06:52-06:55 UTC, activa L1 y L2 y pulsa Find logs. Con New/Old Subnets activado, 192.168.123.0/24 y 10.10.36.0/24 aparecen como anadidos y 6.6.6.6/32 como retirado.

Verificacion con SDK get_network_events, un evento changed por sub-ejercicio 3 eventos

Peticion del SDK

for start, end in [("2026-09-07T06:52:50Z", "2026-09-07T06:52:55Z"),
                   ("2026-09-07T06:53:37Z", "2026-09-07T06:53:42Z"),
                   ("2026-09-07T06:54:12Z", "2026-09-07T06:54:17Z")]:
    net = graph.events.get_network_events(start_time=start, end_time=end)
    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}", "L" + str(e.level_number), e.subnet_type)

Salida del SDK

192.168.123.0/24 changed router2 -1 -> 10 L1 internal
192.168.123.0/24 changed router2 -1 -> 10 L2 internal
10.10.36.0/24 changed router6 -1 -> 10 L2 internal
6.6.6.6/32 changed router6 11 -> -1 L2 internal

El /24 conserva su mascara: no hay colapso a /32. La disponibilidad es una linea up/down que el forwarder descarta; el cambio de coste es el evento changed que devuelve el SDK. El loopback de router2 se ve en L1 y L2, el de router6 solo en L2, y el 6.6.6.6/32 redistribuido se marca como internal.

Reversion: 6a: no ip address 192.168.123.1/24 en router2 interface lo. 6b: no ip address 10.10.36.6/24 en router6 interface lo. 6c: ip route 6.6.6.6/32 192.168.36.3 en router6. Confirma el evento inverso despues de cada uno.

7. Detección de una pérdida de conectividad y su recuperación

Deshabilita router2 eth1, inspecciona el conjunto de eventos correlacionados y luego restaura la interfaz con no shutdown. Como router2-router3 es Level-1-2, cada linea aparece una vez para L1 y otra para L2.

Comando

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

Verifica el cambio. Abre cada fuente disponible en tu entorno y contrástala con los ejemplos de abajo.

Registro del Watcher El conjunto down de L1 y el conjunto de recuperacion de L1 para el enlace router2-router3 down + up

CSV de Watcher

# router2: interface eth1 / shutdown
2026-09-07T06:55:14.754Z,lab-isis01,1,host,router2,down,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,192.168.23.2,192.168.23.1,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:55:14.756Z,lab-isis01,1,metric,router2,changed,old_cost:10,new_cost:-1,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,192.168.23.2,192.168.23.1,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:55:14.760Z,lab-isis01,1,metric,router3,changed,old_cost:10,new_cost:-1,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:55:14.761Z,lab-isis01,1,network,192.168.23.0/24,changed,old_cost:10,new_cost:-1,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,internal,0,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
# router2: interface eth1 / no shutdown
2026-09-07T06:55:39.545Z,lab-isis01,1,metric,router3,changed,old_cost:-1,new_cost:10,router2,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:55:39.607Z,lab-isis01,1,metric,router2,changed,old_cost:-1,new_cost:10,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,192.168.23.2,192.168.23.1,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
Monitoreo La caida como una sola oleada en el flujo

En OSPF/IS-IS Real-Time Monitoring, elige este grafo, define la ventana en torno a 06:55 UTC, activa L1 y L2 y pulsa Find logs. Con Up/Down Links activado, router2 y router3 muestran cada uno su lado del enlace yendo a -1 y volviendo, y 192.168.23.0/24 queda inalcanzable y regresa - dos veces, una por nivel.

Verificacion con SDK get_adjacency_events devuelve los movimientos down/up emparejados 4 movimientos

Peticion del SDK

down = graph.events.get_adjacency_events(
    start_time="2026-09-07T06:55:14Z", end_time="2026-09-07T06:55:16Z")
for e in down["all_host_up_down_events"]:
    print(e.event_object, "detected_by", e.event_detected_by,
          f"{e.old_cost} -> {e.new_cost}", "L" + str(e.level_number))

Salida del SDK

router2 detected_by router3 10 -> -1 L1
router3 detected_by router2 10 -> -1 L1
router2 detected_by router3 10 -> -1 L2
router3 detected_by router2 10 -> -1 L2

host router2 down detectado por router3, ambas metricas dirigidas a -1, 192.168.23.0/24 down - en L1 y L2; luego la recuperacion espejo. En la recuperacion el watcher tambien registra un flap node attr:attached en router2, que Topolograph aun no ingiere.

Pregunta al agente Una pregunta generica de incidente sobre la red

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

Respuesta capturada (Qwen; la redaccion varia entre ejecuciones)

In the last 30 minutes the IS-IS domain (area 49.0001, AS 65100) had 7 event waves detected by watcher lab-isis01, starting at 06:55:15 UTC. Every device that went down recovered within the window.

06:55:15 - router2 and router3 each lost the adjacency between them, at L1 and L2 (router2 detected by router3, router3 detected by router2).

06:55:40 - both routers restored that adjacency at L1 and L2, followed by a few repeated up notifications through 06:56:33.

06:57:23 - router6 lost its L2 adjacency to router3; 06:57:56 - router6 and router3 restored it.

In short: the router2-router3 link dropped at 06:55:15 and came back at 06:55:40; the router3-router6 link dropped at 06:57:23 and recovered at 06:57:56 - both fully converged.

La respuesta nombra la adyacencia caida (router2 - router3), que router3 detecto la perdida de router2 y viceversa, ambas metricas en -1 y la recuperacion - los mismos hechos que las lineas CSV de arriba, a partir de una pregunta que nunca dice "adjacency" ni "failure". Las lineas de router6 son el ejercicio de transito de mas abajo, capturado por la misma ventana de 30 minutos.

Reversion: no shutdown en router2 eth1 restaura la interfaz; espera los eventos de recuperacion host, network y metric en ambos niveles.

8. Informe de un segmento de tránsito de difusión

router6 eth1 se enfrenta a router3 en un circuito de difusion (LAN). router6 lleva isis priority 100 frente a 64 de router3, asi que router6 es el DIS y origina el LSP de pseudonodo. El circuito es solo Level-2, asi que cada linea es L2. router6 eth1 no tiene un isis metric explicito, asi que la base es el valor por defecto 10.

Comando

# cost
sudo docker exec clab-isis01-router6 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'isis metric 66'
# then, separately: shutdown, then no shutdown
sudo docker exec clab-isis01-router6 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'shutdown'
sudo docker exec clab-isis01-router6 vtysh \
  -c 'conf t' -c 'interface eth1' -c 'no shutdown'

Verifica el cambio. Abre cada fuente disponible en tu entorno y contrástala con los ejemplos de abajo.

Registro del Watcher El cambio de coste L2 con sus efectos secundarios en network, mas el par shutdown/recuperacion coste + up/down

CSV de Watcher

# router6: interface eth1 / isis metric 66   (rollback: isis metric 10)
2026-09-07T06:56:47.139Z,lab-isis01,2,network,192.168.36.0/24,changed,old_cost:10,new_cost:66,router6,07Sep2026_06h49m43s_6_hosts,49.0001,65100,internal,0,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:56:47.140Z,lab-isis01,2,metric,router3,changed,old_cost:10,new_cost:66,router6,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:57:04.561Z,lab-isis01,2,metric,router3,changed,old_cost:66,new_cost:10,router6,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
# router6: interface eth1 / shutdown
2026-09-07T06:57:22.037Z,lab-isis01,2,network,192.168.36.0/24,changed,old_cost:10,new_cost:-1,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,internal,0,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:57:22.055Z,lab-isis01,2,host,router6,down,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,192.168.36.3,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:57:22.055Z,lab-isis01,2,metric,router6,changed,old_cost:10,new_cost:-1,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,192.168.36.3,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
# router6: interface eth1 / no shutdown
2026-09-07T06:57:55.962Z,lab-isis01,2,host,router6,up,router3,07Sep2026_06h49m43s_6_hosts,49.0001,65100,192.168.36.3,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
2026-09-07T06:57:55.973Z,lab-isis01,2,metric,router3,changed,old_cost:-1,new_cost:66,router6,07Sep2026_06h49m43s_6_hosts,49.0001,65100,,,e50b9d98-aa86-11f1-92b0-46ed252a638b,0100.1001.0001
Monitoreo El cambio de coste de transito y la caida de router6, solo L2

En OSPF/IS-IS Real-Time Monitoring, elige este grafo, define la ventana en torno a 06:56-06:58 UTC, activa L2 y pulsa Find logs. El object del evento metric es router3 con event_detected_by router6, y el cambio es solo L2 - el circuito es level-2-only. En el shutdown el flujo muestra host router6 down y 192.168.36.0/24 inalcanzable, luego la recuperacion.

Verificacion con SDK get_adjacency_events para el coste de transito y el shutdown/recuperacion solo L2

Peticion del SDK

cost = graph.events.get_adjacency_events(
    start_time="2026-09-07T06:56:46Z", end_time="2026-09-07T06:56:49Z")
for e in cost["adjacency_cost_change_events"]:
    print("cost", e.event_object, e.event_detected_by, f"{e.old_cost} -> {e.new_cost}", "L" + str(e.level_number))
flap = graph.events.get_adjacency_events(
    start_time="2026-09-07T06:57:21Z", end_time="2026-09-07T06:58:00Z")
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}", "L" + str(e.level_number))

Salida del SDK

cost router3 router6 10 -> 66 L2
updown router6 router3 10 -> -1 L2
updown router6 router3 -1 -> 10 L2
updown router3 router6 -1 -> 10 L2

En el segmento de difusion el evento metric sigue nombrando al vecino (router3), detectado por router6, y solo en L2; el cambio de metrica tambien mueve 192.168.36.0/24 y las dos /127 IPv6. El shutdown es host router6 down en L2, luego la recuperacion.

Pregunta al agente Una pregunta generica de incidente sobre router6

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

Una respuesta valida nombra la metrica de transito router6 -> router3 moviendose 10 -> 66 y de vuelta, luego la conexion de router6 cayendo (metrica -1 en L2) y recuperandose, sin que la pregunta mencione coste, DIS ni shutdown.

Reversion: isis metric 10 (o no isis metric) en router6 eth1 restaura la base 10; no shutdown restaura la adyacencia. Confirma los eventos metric inversos.

9. Cargar el laboratorio IS-IS de 13 routers

El laboratorio de seis routers permite examinar eventos de Watcher y atributos TE, pero no ofrece suficientes rutas alternativas para ejercicios de CSPF. Usa el laboratorio 13-hosts-demo-isis: despliégalo o evita el despliegue y descarga la LSDB lista demo_isis_LSDB.txt.

Sube la LSDB a Topolograph como archivo FRR IS-IS: la capturada en r30 o demo_isis_LSDB.txt descargado. El grafo resultante tiene 13 nodos y 52 aristas dirigidas: 12 de nivel 1 y 40 de nivel 2. Cincuenta aristas contienen atributos TE; las dos direcciones de r110-r111 no los contienen.

Comando

cd containerlab/13-hosts-demo-isis
sudo clab deploy --topo 13-hosts-demo-isis.clab.yml
sudo docker exec clab-13-hosts-demo-isis-r30 vtysh -c 'show isis database detail'

Peticion del SDK

from topolograph import Topolograph

topo = Topolograph(url="http://<your-topolograph>:8080", username="<email>", password="<password>")
graph = topo.graphs.upload(open("demo_isis_LSDB.txt").read(), vendor="FRR", protocol="isis")
print(graph.graph_time, graph.hosts)
print(graph.edges_list(per_page=200)["pagination"]["total"])
print(graph.edges_list(is_te_link=True, per_page=200)["pagination"]["total"])
print(graph.edges_list(is_te_link=False, per_page=200)["pagination"]["total"])

Salida del SDK

<new graph time> {'count': 13}
52
50
2
Observaciones requeridas Hechos a confirmar antes de continuar
  • El campo hosts del grafo cargado contiene {'count': 13} y el número total de aristas es 52.
  • edges_list(is_te_link=True) devuelve 50 aristas; edges_list(is_te_link=False) devuelve las dos direcciones de r110-r111.

10. Leer los valores TE antes del cálculo

Examina los atributos TE del grafo antes de aplicar una restricción. Identifica los grupos administrativos gold y red, las métricas TE altas, el pequeño pool de ancho de banda de prioridad 7, los enlaces con SRLG y los enlaces sin TE. Un cálculo normal de ruta mínima no indica si un enlace es apto para un túnel.

En este grafo, gold (0x00000002) coincide con 10 aristas dirigidas, red (0x00000004) con 2, temetric__gt=30 con 4, unreserved_bw_7__lt=1000000 con 2 e is_te_link=False devuelve las dos direcciones de r110-r111.

Comando

# Ejecuta estos filtros con el SDK sobre el grafo del paso 1

Peticion del SDK

for query in (
    {'admin_group': '0x00000002'},
    {'admin_group': '0x00000004'},
    {'temetric__gt': 30},
    {'unreserved_bw_7__lt': 1_000_000},
    {'is_te_link': False},
):
    result = graph.edges_list(per_page=200, **query)
    print(query, result['pagination']['total'])

Salida del SDK

{'admin_group': '0x00000002'} 10
{'admin_group': '0x00000004'} 2
{'temetric__gt': 30} 4
{'unreserved_bw_7__lt': 1000000} 2
{'is_te_link': False} 2
Observaciones requeridas Hechos a confirmar antes de continuar
  • Ocho aristas de r10-r100, r100-r110 y r100-r111 contienen datos SRLG; los dos enlaces paralelos r10-r100 conservan sus propios grupos.
  • El valor de texto is_te_link='yes' se rechaza con HTTP 400: Wrong type, expected 'boolean'.

11. Registrar la ruta sin restricciones

Calcula la ruta sin restricciones de r10 a r14 y consérvala como referencia para los ejercicios siguientes. Así podrás comparar cada restricción nueva con una ruta conocida.

La ruta de referencia es r10 r100 r110 r14, con coste 30. Utiliza el enlace paralelo eth2 entre r10 y r100.

Comando

No cambies routers: ejecuta la consulta CSPF de referencia.

Peticion del SDK

print(graph.cspf_path('r10', 'r14'))

Salida del SDK

{'cost': 30, 'path': ['r10', 'r100', 'r110', 'r14'], 'reason': ''}
Observaciones requeridas Hechos a confirmar antes de continuar
  • Los cuatro pasos siguientes moverán esta ruta con una sola restricción cada vez.

12. Evitar grupos de riesgo compartido

Utiliza srlg_exclude para evitar un enlace o conducto compartido en mantenimiento. La ruta de referencia r10 r100 r110 r14 cuesta 30: su primer salto pertenece a SRLG 300 y ambos primeros saltos paralelos comparten SRLG 100.

Al excluir 300 se mantienen los mismos routers, pero se selecciona eth1 y el coste sube a 35. Excluir 100 produce r10 r11 r100 r110 r14 con coste 40; excluir 200 produce r10 r100 r101 r111 r14 con coste 45; excluir 100 y 200 produce r10 r11 r101 r111 r14 con coste 50.

Comando

Cambia solamente la lista `srlg_exclude`.

Peticion del SDK

for groups in ([300], [100], [200], [100, 200]):
    print(groups, graph.cspf_path('r10', 'r14', srlg_exclude=groups))

Salida del SDK

[300] {'cost': 35, 'path': ['r10', 'r100', 'r110', 'r14'], 'reason': ''}
[100] {'cost': 40, 'path': ['r10', 'r11', 'r100', 'r110', 'r14'], 'reason': ''}
[200] {'cost': 45, 'path': ['r10', 'r100', 'r101', 'r111', 'r14'], 'reason': ''}
[100, 200] {'cost': 50, 'path': ['r10', 'r11', 'r101', 'r111', 'r14'], 'reason': ''}
Observaciones requeridas Hechos a confirmar antes de continuar
  • El filtro elimina enlaces; no cambia las métricas IGP.

13. Elegir la ruta por ancho de banda y prioridad

Solicita un túnel de 8 Mbit con distintas prioridades de establecimiento. La ruta de referencia r10 r100 r110 r14 cuesta 30, pero r100-r110 anuncia solo 4 Mbit en el pool de prioridad 7 y 10 Mbit en las prioridades 0-3.

Con prioridad 7, CSPF mueve el túnel a r10 r100 r111 r14, con coste 40. Con prioridad 0 mantiene r10 r100 r110 r14, con coste 30. Una solicitud de 20 Mbit no cabe en ninguna ruta disponible y devuelve un rechazo por restricciones.

Comando

Cambia `bandwidth` y `setup_priority` en la consulta CSPF.

Peticion del SDK

for priority in (7, 0):
    print(priority, graph.cspf_path('r10', 'r14', bandwidth='8M', setup_priority=priority))
print(graph.cspf_path('r10', 'r14', bandwidth='20M'))

Salida del SDK

7 {'cost': 40, 'path': ['r10', 'r100', 'r111', 'r14'], 'reason': ''}
0 {'cost': 30, 'path': ['r10', 'r100', 'r110', 'r14'], 'reason': ''}
{'cost': None, 'path': [], 'reason': 'no path satisfies the requested constraints'}
Observaciones requeridas Hechos a confirmar antes de continuar
  • La prioridad selecciona el pool de ancho de banda anunciado, no la métrica IGP.

14. Calcular usando la métrica TE

Recalcula la ruta con metric_type='te' después de comprobar que cada enlace candidato tenga una métrica TE. La ruta IGP r10 r100 r110 r14 cuesta 30, pero r100-r110 tiene métrica TE 100 y r100-r111 tiene métrica TE 10.

CSPF selecciona r10 r100 r111 r14 con coste TE 30. La misma ruta tiene coste IGP 40, lo que permite ver el efecto de la métrica elegida.

Comando

Usa `metric_type='te'`; no cambies routers.

Peticion del SDK

print(graph.cspf_path('r10', 'r14', metric_type='te'))

Salida del SDK

{'cost': 30, 'path': ['r10', 'r100', 'r111', 'r14'], 'reason': ''}
Observaciones requeridas Hechos a confirmar antes de continuar
  • Sin métrica TE en un enlace, el cálculo usa IGP para ese enlace.

15. Incluir o excluir bits de admin group

Utiliza los bits de grupos administrativos para exigir o evitar clases de enlace. El bit 1 es gold y el bit 2 es red. La ruta sin restricciones de r13 a r15 es r13 r100 r110 r15, con coste 30.

Exigir gold con admin_include_all=['1'] cambia esa ruta a r13 r101 r111 r15, con coste 45; de r10 a r14 produce r10 r101 r111 r14, con coste 55. Evitar red con admin_exclude_any=['2'] produce r13 r100 r111 r15, con coste 40.

Comando

Pasa números de bit, no máscaras hexadecimales.

Peticion del SDK

print(graph.cspf_path('r13', 'r15', admin_include_all=['1']))
print(graph.cspf_path('r10', 'r14', admin_include_all=['1']))
print(graph.cspf_path('r13', 'r15', admin_exclude_any=['2']))

Salida del SDK

{'cost': 45, 'path': ['r13', 'r101', 'r111', 'r15'], 'reason': ''}
{'cost': 55, 'path': ['r10', 'r101', 'r111', 'r14'], 'reason': ''}
{'cost': 40, 'path': ['r13', 'r100', 'r111', 'r15'], 'reason': ''}
Observaciones requeridas Hechos a confirmar antes de continuar
  • El bit 1 es 0x00000002, el bit 2 0x00000004; el bit 0 es el menos significativo.

16. Limitar CSPF a un nivel IS-IS

Define level=1 o level=2 para calcular dentro de una sola topología IS-IS y comprueba que ambos extremos pertenezcan a ella. Entre r30 y r31, la ruta directa de nivel 1 cuesta 10, mientras que la ruta de nivel 2 r30 r10 r11 r31 cuesta 40.

La ruta r130-r131 cuesta 10 en el nivel 1, pero devuelve src/dst not found en el nivel 2. La topología combinada tiene una ruta r130-r15 de coste 50, pero ningún nivel individual la contiene porque cruza el límite entre niveles.

Comando

Añade `level=1` o `level=2` a la consulta CSPF.

Peticion del SDK

for level in (None, 1, 2):
    print(level, graph.cspf_path('r30', 'r31', level=level))
print(graph.cspf_path('r130', 'r131', level=2))

Salida del SDK

None {'cost': 10, 'path': ['r30', 'r31'], 'reason': ''}
1 {'cost': 10, 'path': ['r30', 'r31'], 'reason': ''}
2 {'cost': 40, 'path': ['r30', 'r10', 'r11', 'r31'], 'reason': ''}
{'cost': None, 'path': [], 'reason': 'src/dst not found'}
Observaciones requeridas Hechos a confirmar antes de continuar
  • Un grafo antiguo sin datos por nivel devuelve HTTP 422 y isis_level_calculation_unavailable; hay que cargarlo de nuevo.

17. Distinguir una restricción imposible de un endpoint ausente

Lee reason cuando CSPF devuelva un path vacío. La solicitud de 20 Mbit del paso 5 tiene extremos válidos, pero ninguna ruta dispone de ancho de banda suficiente; una solicitud de nivel 1 entre r130 y r15 no tiene una topología de un solo nivel que contenga ambos extremos.

La solicitud de ancho de banda devuelve no path satisfies the requested constraints; la solicitud con el nivel incorrecto devuelve src/dst not found. Cada fallo requiere una acción correctiva diferente.

Comando

Repite las peticiones fallidas de los pasos 5 y 8.

Peticion del SDK

print(graph.cspf_path('r10', 'r14', bandwidth='20M'))
print(graph.cspf_path('r130', 'r15', level=1))

Salida del SDK

{'cost': None, 'path': [], 'reason': 'no path satisfies the requested constraints'}
{'cost': None, 'path': [], 'reason': 'src/dst not found'}
Observaciones requeridas Hechos a confirmar antes de continuar
  • El primer mensaje pide relajar bandwidth, affinity o SRLG; el segundo indica endpoints ausentes.

18. Editar un enlace y recalcular

Modela el mantenimiento de r100-r110 sin cambiar los routers. En una copia desechable del grafo, utiliza update_edge para añadir SRLG 999 a la arista de nivel 2, recalcula excluyendo ese grupo y después limpia el SRLG.

Excluir 999 cambia la ruta r10 r100 r110 r14, con coste 30, por r10 r100 r111 r14, con coste 40; limpiar el SRLG restaura la ruta de referencia. Una arista exclusiva de nivel 2 rechaza isis_level=1. Como replace_edge elimina los datos por nivel, una solicitud CSPF con nivel explícito devuelve HTTP 422 después de una sustitución completa.

Comando

# Usa un grafo desechable: la edición queda guardada
# Busca primero el id de la arista con edges_list(include=['edge_key'])

Peticion del SDK

edge = next(e for e in graph.edges_list(include=['edge_key'], per_page=200)['items']
             if e['src'] == 'r100' and e['dst'] == 'r110')
print(graph.update_edge(edge['id'], isis_level=2, srlg=[999]))
print(graph.cspf_path('r10', 'r14', srlg_exclude=[999]))
print(graph.update_edge(edge['id'], isis_level=2, srlg=[]))
print(graph.cspf_path('r10', 'r14'))

Salida del SDK

... srlg: [999] ...
{'cost': 40, 'path': ['r10', 'r100', 'r111', 'r14'], 'reason': ''}
... srlg: [] ...
{'cost': 30, 'path': ['r10', 'r100', 'r110', 'r14'], 'reason': ''}
Observaciones requeridas Hechos a confirmar antes de continuar
  • update_edge modifica solo los campos indicados; replace_edge reescribe la arista completa. No uses ninguno de los dos sobre el grafo de referencia que quieras conservar.

19. Colocar LSP y comprobar el ancho de banda restante

Añade cuatro túneles de 4 Mbit de r10 a r14, uno por uno, y examina el ancho de banda residual y la ruta seleccionada después de cada colocación. Antes de colocar el primero, está disponible el pool de prioridad 7 de 4 Mbit de la ruta de referencia r10 r100 r110 r14.

T1 utiliza r10 r100 r110 r14, con coste 30; T2 utiliza r10 r100 r111 r14, con coste 40; T3 utiliza el otro enlace r10-r100, con coste 45; y T4 utiliza r10 r11 r101 r110 r14, con coste 55. Elimina todos los LSP de prueba al terminar el ejercicio.

Comando

# No cambies los routers: crea cuatro túneles RSVP-TE de 4 Mbit con el SDK

Peticion del SDK

for name in ('T1', 'T2', 'T3', 'T4'):
    lsp = graph.add_lsp({
        'name': name, 'src': 'r10', 'dst': 'r14', 'bandwidth': '4M',
        'paths': {'primary': {'role': 'primary', 'bandwidth': '4M'}},
    })
    primary = lsp['paths']['primary']
    print(name, primary['path'], primary['cost'])
print(graph.edges_list(include=['lsp_left_bw', 'lsps'], per_page=200)['items'])
graph.delete_lsps()

Salida del SDK

T1 ['r10', 'r100', 'r110', 'r14'] 30
T2 ['r10', 'r100', 'r111', 'r14'] 40
T3 ['r10', 'r100', 'r110', 'r14'] 45
T4 ['r10', 'r11', 'r101', 'r110', 'r14'] 55
... lsp_left_bw_7 y lsps para cada arista ...
{'deleted': 4}
Observaciones requeridas Hechos a confirmar antes de continuar
  • Después de cada colocación, usa edges_list(include=['lsp_left_bw', 'lsps']) para ver qué pool provocó el siguiente cambio de ruta.
  • Llama siempre a delete_lsps() durante la limpieza; la colocación de LSP modifica el grafo guardado.
Topolograph 2.73 📣 ¡Únete a la comunidad!