Files
infix/test/case/routing/ospf_multiarea/test.py
T
Joachim Wiberg 1bbd80d8c7 test: drop leading ietf/infix prefix from directories
Let's drop the leading IETF or Infix prefixes from tests.  Initially the
idea was to mimnic the YANG models, but it's difficult to navigate and
does not provide any real benefit to developers or end-users.

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
2025-11-20 20:23:23 +01:00

677 lines
30 KiB
Python
Executable File

#!/usr/bin/env python3
#
#
# 10.0.0.1/32 (lo)
# |
# +------+---------+ .1 10.0.12.0/30 .2+--------------------+
# | R1 +-----------------------------+ R2 |
# | | AREA0 | +-10.0.0.2/32 (lo)
# +-------+--------+--.1 .1 -+--------+-----------+
# .2 | \--- ---/ |.1
# | \--- ----/ |
# 10.0.41.0 /30 | AREA2 \--- ---/ | 10.0.23.0/30
# | 10.0.24.0/30 ---/\--- 10.0.13.0/30 |
# | ----/ \--- AREA1 |
# .1 | .2 ---/ \--- .2 |.2
# +-------+--------+-/ \-+--------+----------+
# | R4 .2 | | R3 |
# | +---------+ .1| +-10.0.0.3/32 (lo)
# +------+---------+.1 | 192.168.4.0/24 +-------------------+
# | |.2
# 10.0.0.4/32 (lo) +-------+
# | |
# | PC |
# | |
# +-------+
#
#
"""OSPF with multiple areas
This test evaluates various OSPF features across three areas (one NSSA area, with no summary)
to ensure that route distribution is deterministic (based on cost). It also tests link
failures using BFD, though BFD is not yet implemented in test framework (Infamy).
This test also verifies broadcast and point-to-point interface types on /30 network, and
explicit router-id.
....
+-------------+ +---------------+ +-------------+ +---------------+
| R1 | | R2 | | R3 | | R4 |
| 10.0.0.1/32 | | 10.0.0.2/32 | | 10.0.0.3/32 | | 10.0.0.4/32 |
| (lo) | | 11.0.9.1/24 | | (lo) | | (lo) |
+-------------+ | 11.0.10.1/24 | +-------------+ +---------------+
| 11.0.11.1/24 |
| 11.0.12.1/24 |
| 11.0.13.1/24 |
| 11.0.14.1/24 |
| 11.0.15.1/24 |
| (lo) |
+---------------+
....
"""
import infamy
import infamy.route as route
from infamy.util import until, parallel
def config_target1(target, ring1, ring2, cross):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": ring1,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.12.1",
"prefix-length": 30
}]
}
},
{
"name": ring2,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.41.2",
"prefix-length": 30
}]
}
},
{
"name": cross,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.13.1",
"prefix-length": 30
}]
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [
{
"ip": "11.0.8.1",
"prefix-length": 24
},
{
"ip": "10.0.0.1",
"prefix-length": 32
}
]
}
}
]
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [
{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"explicit-router-id": "10.0.0.1",
"areas": {
"area": [
{
"area-id": "0.0.0.0",
"interfaces": {
"interface": [
{
"bfd": {
"enabled": True
},
"name": ring1,
"hello-interval": 1,
"enabled": True
}
]
}
},
{
"area-id": "0.0.0.1",
"area-type": "nssa-area",
"summary": False,
"interfaces": {
"interface": [
{
"bfd": {
"enabled": True
},
"name": cross,
"hello-interval": 1,
"enabled": True,
"cost": 2000
},
{
"name": "lo",
"enabled": True
}
]
}
},
{
"area-id": "0.0.0.2",
"interfaces": {
"interface": [
{
"bfd": {
"enabled": True
},
"name": ring2,
"hello-interval": 1,
"enabled": True,
"interface-type": "point-to-point"
}
]
}
}
]
}
}
}
]
}
}
},
"ietf-system": {
"system": {
"hostname": "R1"
}
}
})
def config_target2(target, ring1, ring2, cross):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": ring1,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.23.1",
"prefix-length": 30
}]
}
},
{
"name": ring2,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.12.2",
"prefix-length": 30
}]
}
},
{
"name": cross,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.24.1",
"prefix-length": 30
}]
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [
{
"ip": "10.0.0.2",
"prefix-length": 32
},
{
"ip": "11.0.9.1",
"prefix-length": 24
},
{
"ip": "11.0.10.1",
"prefix-length": 24
},
{
"ip": "11.0.11.1",
"prefix-length": 24
},
{
"ip": "11.0.12.1",
"prefix-length": 24
},
{
"ip": "11.0.13.1",
"prefix-length": 24
},
{
"ip": "11.0.14.1",
"prefix-length": 24
},
{
"ip": "11.0.15.1",
"prefix-length": 24
}
]
}
}
]
}
},
"ietf-system": {
"system": {
"hostname": "R2"
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [
{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"explicit-router-id": "1.1.1.1",
"areas": {
"area": [
{
"area-id": "0.0.0.0",
"interfaces": {
"interface": [
{
"bfd": {
"enabled": True
},
"name": ring2,
"hello-interval": 1,
"enabled": True
},
{
"name": "lo",
"enabled": True
}
]
}
},
{
"area-id": "0.0.0.1",
"area-type": "nssa-area",
"summary": False,
"interfaces": {
"interface": [
{
"bfd": {
"enabled": True
},
"name": ring1,
"hello-interval": 1,
"enabled": True
}
]
}
},
{
"area-id": "0.0.0.2",
"interfaces": {
"interface": [
{
"bfd": {
"enabled": True
},
"name": cross,
"hello-interval": 1,
"cost": 2000,
"enabled": True
}
]
}
}
]
}
}
}
]
}
}
}
})
def config_target3(target, ring2, cross, link):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": ring2,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.23.2",
"prefix-length": 30
}]
}
},
{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.3.1",
"prefix-length": 24
}]
}
},
{
"name": cross,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.13.2",
"prefix-length": 30
}]
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [{
"ip": "10.0.0.3",
"prefix-length": 32
}]
}
}
]
}
},
"ietf-system": {
"system": {
"hostname": "R3"
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"explicit-router-id": "10.0.0.3",
"areas": {
"area": [{
"area-id": "0.0.0.1",
"area-type": "nssa-area",
"summary": False,
"interfaces": {
"interface": [{
"bfd": {
"enabled": True
},
"name": cross,
"hello-interval": 1,
"enabled": True,
"cost": 2000
}, {
"bfd": {
"enabled": True
},
"name": ring2,
"hello-interval": 1,
"enabled": True
}, {
"name": link,
"enabled": True,
"passive": True
}, {
"name": "lo",
"enabled": True
}]
}
}]
}
}
}]
}
}
}
})
def config_target4(target, ring1, cross, link):
target.put_config_dicts({
"ietf-interfaces": {
"interfaces": {
"interface": [
{
"name": ring1,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.41.1",
"prefix-length": 30
}]}
},
{
"name": cross,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "10.0.24.2",
"prefix-length": 30
}]}
},
{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.4.1",
"prefix-length": 24
}]
}
},
{
"name": "lo",
"enabled": True,
"ipv4": {
"address": [{
"ip": "10.0.0.4",
"prefix-length": 32
}]
}
}
]
}
},
"ietf-system": {
"system": {
"hostname": "R4"
}
},
"ietf-routing": {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [
{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"explicit-router-id": "10.0.0.4",
"redistribute": {
"redistribute": [{
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.2",
"interfaces": {
"interface": [{
"bfd": {
"enabled": True
},
"name": ring1,
"hello-interval": 1,
"enabled": True,
"interface-type": "point-to-point"
}, {
"bfd": {
"enabled": True
},
"name": cross,
"hello-interval": 1,
"enabled": True,
"cost": 5000
}, {
"name": "lo",
"enabled": True
}]
}
}]
}
}
}
]
}
}
}
})
def disable_link(target, link):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [
{
"name": link,
"enabled": False
}]
}
})
with infamy.Test() as test:
with test.step("Set up topology and attach to target DUTs"):
env = infamy.Env()
R1 = env.attach("R1", "mgmt")
R2 = env.attach("R2", "mgmt")
R3 = env.attach("R3", "mgmt")
R4 = env.attach("R4", "mgmt")
_, R1ring1 = env.ltop.xlate("R1", "ring1")
_, R1ring2 = env.ltop.xlate("R1", "ring2")
_, R2ring1 = env.ltop.xlate("R2", "ring1")
_, R2ring2 = env.ltop.xlate("R2", "ring2")
_, R3ring2 = env.ltop.xlate("R3", "ring2")
_, R4ring1 = env.ltop.xlate("R4", "ring1")
_, R3data = env.ltop.xlate("R3", "data")
_, R4data = env.ltop.xlate("R4", "data")
_, R1cross = env.ltop.xlate("R1", "cross")
_, R2cross = env.ltop.xlate("R2", "cross")
_, R3cross = env.ltop.xlate("R3", "cross")
_, R4cross = env.ltop.xlate("R4", "cross")
with test.step("Configure targets"):
parallel(config_target1(R1, R1ring1, R1ring2, R1cross),
config_target2(R2, R2ring1, R2ring2, R2cross),
config_target3(R3, R3ring2, R3cross, R3data),
config_target4(R4, R4ring1, R4cross, R4data))
with test.step("Wait for all neighbors to peer"):
print("Waiting for neighbors to peer")
until(lambda: route.ospf_get_neighbor(R1, "0.0.0.0", R1ring1, "1.1.1.1"), attempts=200)
until(lambda: route.ospf_get_neighbor(R1, "0.0.0.1", R1cross, "10.0.0.3"), attempts=200)
until(lambda: route.ospf_get_neighbor(R2, "0.0.0.1", R2ring1, "10.0.0.3"), attempts=200)
until(lambda: route.ospf_get_neighbor(R2, "0.0.0.0", R2ring2, "10.0.0.1"), attempts=200)
until(lambda: route.ospf_get_neighbor(R2, "0.0.0.2", R2cross, "10.0.0.4"), attempts=200)
with test.step("Wait for routes from OSPF on all routers"):
print("Waiting for routes from OSPF")
until(lambda: route.ipv4_route_exist(R1, "10.0.0.2/32", nexthop="10.0.12.2", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R1, "10.0.0.3/32", nexthop="10.0.13.2", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R1, "10.0.0.4/32", nexthop="10.0.41.1", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R1, "192.168.4.0/24", nexthop="10.0.41.1", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R1, "10.0.24.0/30", nexthop="10.0.41.1", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "10.0.0.1/32", nexthop="10.0.23.2", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "10.0.0.3/32", nexthop="10.0.23.2", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "10.0.0.4/32", nexthop="10.0.24.2", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R3, "0.0.0.0/0", nexthop="10.0.23.1", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.41.2", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "10.0.13.0/30", nexthop="10.0.23.2", proto="ietf-ospf:ospfv2"), attempts=200)
with test.step("Verify Area 0.0.0.1 on R3 is NSSA area"):
assert(route.ospf_is_area_nssa(R3, "0.0.0.1"))
with test.step("Verify R1:ring2 is of type point-to-point"):
assert(route.ospf_get_interface_type(R1, "0.0.0.2", R1ring2) == "point-to-point")
with test.step("Verify R4:ring1 is of type point-to-point"):
assert(route.ospf_get_interface_type(R4, "0.0.0.2", R4ring1) == "point-to-point")
with test.step("Verify on R3, there are no routes beyond 10.0.23.1, just a default route"):
# Should be only default route out of the area.
parallel(until(lambda: route.ipv4_route_exist(R3, "0.0.0.0/0"), attempts=200),
until(lambda: route.ipv4_route_exist(R3, "10.0.12.0/30") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "10.0.12.0/30") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "11.0.8.0/24") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "11.0.9.0/24") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "11.0.10.0/24") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "11.0.11.0/24") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "11.0.12.0/24") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "11.0.13.0/24") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "11.0.14.0/24") is False, attempts=5),
until(lambda: route.ipv4_route_exist(R3, "11.0.15.0/24") is False, attempts=5))
_, hport0 = env.ltop.xlate("PC", "data3")
with infamy.IsolatedMacVlan(hport0) as ns0:
with test.step("Testing connectivity through NSSA area, from PC:data3 to 11.0.8.1"):
ns0.addip("192.168.3.2")
ns0.addroute("0.0.0.0/0", "192.168.3.1")
ns0.must_reach("11.0.8.1")
_, hport0 = env.ltop.xlate("PC", "data4")
with infamy.IsolatedMacVlan(hport0) as ns0:
ns0.addip("192.168.4.2")
ns0.addroute("0.0.0.0/0", "192.168.4.1")
with test.step("Verify that the route to 10.0.0.3 from PC:data4, go through 10.0.41.2"):
trace = ns0.traceroute("10.0.0.3")
assert len(trace) == 3
assert trace[1][1] == "10.0.41.2"
assert trace[2][1] == "10.0.0.3"
with test.step("Break link R1:ring2 --- R4:ring1"):
# Here we should test with link breakers, to test BFD
# recouppling, for now disable the link
disable_link(R1, R1ring2)
with test.step("Verify that the route to 10.0.0.3 from PC:data4, go through 10.0.24.1"):
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.24.1", proto="ietf-ospf:ospfv2"), attempts=100)
until(lambda: route.ipv4_route_exist(R4, "10.0.0.3/32", nexthop="10.0.41.2") is False, attempts=10)
trace = ns0.traceroute("10.0.0.3")
assert len(trace) == 3
assert trace[1][1] == "10.0.24.1"
assert trace[2][1] == "10.0.0.3"
ns0.must_reach("10.0.0.3")
test.succeed()