test: verify ospf neighbors in setup with non-ospf interface

Extend OSPF basic with a regression test for issue #1169

Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg
2025-10-03 12:55:43 +02:00
parent 4cfd0eb7a0
commit 5f51ef065e
5 changed files with 200 additions and 108 deletions
+9 -2
View File
@@ -4,8 +4,14 @@ ifdef::topdoc[:imagesdir: {topdoc}../../test/case/ietf_routing/ospf_basic]
==== Description
Very basic OSPF test just test that OSPF sends HELLO packets between the DUTs
and that they exchange routes, ending with a simple connectivity check.
Verifies basic OSPF functionality by configuring two routers (R1 and R2)
with OSPF on their interconnecting link. The test ensures OSPF
neighbors are established, routes are exchanged between the routers, and
end-to-end connectivity is achieved.
An end-device (HOST) is connected to R2 on an interface without OSPF enabled.
This verifies that OSPF status information remains accessible when a router
has non-OSPF interfaces.
==== Topology
@@ -16,6 +22,7 @@ image::topology.svg[OSPF Basic topology, align=center, scaledwidth=75%]
. Set up topology and attach to target DUTs
. Configure targets
. Wait for OSPF routes
. Verify R2 OSPF neighbors with non-OSPF interface
. Test connectivity from PC:data to 192.168.200.1
+105 -63
View File
@@ -1,15 +1,24 @@
#!/usr/bin/env python3
"""
OSPF Basic
"""OSPF Basic
Verifies basic OSPF functionality by configuring two routers (R1 and R2)
with OSPF on their interconnecting link. The test ensures OSPF
neighbors are established, routes are exchanged between the routers, and
end-to-end connectivity is achieved.
An end-device (HOST) is connected to R2 on an interface without OSPF enabled.
This verifies that OSPF status information remains accessible when a router
has non-OSPF interfaces.
Very basic OSPF test just test that OSPF sends HELLO packets between the DUTs
and that they exchange routes, ending with a simple connectivity check.
"""
# TODO: Remove HOST node once Infamy supports unconnected ports in topologies
import infamy
import infamy.route as route
from infamy.util import until, parallel
def config_target1(target, data, link):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
@@ -30,8 +39,8 @@ def config_target1(target, data, link):
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.50.1",
"prefix-length": 24
"ip": "192.168.50.1",
"prefix-length": 24
}]
}
},
@@ -40,8 +49,8 @@ def config_target1(target, data, link):
"enabled": True,
"ipv4": {
"address": [{
"ip": "192.168.100.1",
"prefix-length": 32
"ip": "192.168.100.1",
"prefix-length": 32
}]
}
}
@@ -56,38 +65,34 @@ def config_target1(target, data, link):
target.put_config_dict("ietf-routing", {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [
{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "192.168.33.1/32",
"next-hop": {
"special-next-hop": "blackhole"
}
}]
}
"control-plane-protocol": [{
"type": "infix-routing:static",
"name": "default",
"static-routes": {
"ipv4": {
"route": [{
"destination-prefix": "192.168.33.1/32",
"next-hop": {
"special-next-hop": "blackhole"
}
}]
}
},
{
}
}, {
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
"redistribute": {
"redistribute": [{
"protocol": "static"
},
{
}, {
"protocol": "connected"
}]
},
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces":
{
"interfaces": {
"interface": [{
"enabled": True,
"name": link,
@@ -103,35 +108,43 @@ def config_target1(target, data, link):
}
})
def config_target2(target, link):
def config_target2(target, link, data):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [
{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.50.2",
"prefix-length": 24
}]
}
},
{
"name": "lo",
"enabled": True,
"forwarding": True,
"ipv4": {
"address": [{
"ip": "192.168.200.1",
"prefix-length": 32
}]
}
}
]
}
})
"interfaces": {
"interface": [{
"name": link,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.50.2",
"prefix-length": 24
}]
}
}, {
"name": data,
"enabled": True,
"ipv4": {
"forwarding": True,
"address": [{
"ip": "192.168.60.1",
"prefix-length": 24
}]
}
}, {
"name": "lo",
"enabled": True,
"forwarding": True,
"ipv4": {
"address": [{
"ip": "192.168.200.1",
"prefix-length": 32
}]
}
}]
}
})
target.put_config_dict("ietf-system", {
"system": {
@@ -141,8 +154,7 @@ def config_target2(target, link):
target.put_config_dict("ietf-routing", {
"routing": {
"control-plane-protocols": {
"control-plane-protocol": [
{
"control-plane-protocol": [{
"type": "infix-routing:ospfv2",
"name": "default",
"ospf": {
@@ -154,7 +166,7 @@ def config_target2(target, link):
"areas": {
"area": [{
"area-id": "0.0.0.0",
"interfaces":{
"interfaces": {
"interface": [{
"enabled": True,
"name": link,
@@ -165,31 +177,61 @@ def config_target2(target, link):
}]
}
}
}
]
}]
}
}
})
def config_host(target, link):
target.put_config_dict("ietf-interfaces", {
"interfaces": {
"interface": [{
"name": link,
"enabled": True,
"ipv4": {
"address": [{
"ip": "192.168.60.2",
"prefix-length": 24
}]
}
}]
}
})
target.put_config_dict("ietf-system", {
"system": {
"hostname": "HOST"
}
})
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")
HOST = env.attach("HOST", "mgmt")
with test.step("Configure targets"):
_, R1data = env.ltop.xlate("R1", "data")
_, R2link = env.ltop.xlate("R2", "link")
_, R1link= env.ltop.xlate("R1", "link")
_, R1link = env.ltop.xlate("R1", "link")
_, R2data = env.ltop.xlate("R2", "data")
_, HOSTlink = env.ltop.xlate("HOST", "link")
parallel(config_target1(R1, R1data, R1link),
config_target2(R2, R2link))
config_target2(R2, R2link, R2data),
config_host(HOST, HOSTlink))
with test.step("Wait for OSPF routes"):
print("Waiting for OSPF routes..")
until(lambda: route.ipv4_route_exist(R1, "192.168.200.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "192.168.100.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
until(lambda: route.ipv4_route_exist(R2, "192.168.33.1/32", proto="ietf-ospf:ospfv2"), attempts=200)
with test.step("Verify R2 OSPF neighbors with non-OSPF interface"):
# Regression test for #1169
assert route.ospf_has_neighbors(R2)
with test.step("Test connectivity from PC:data to 192.168.200.1"):
_, hport0 = env.ltop.xlate("PC", "data")
with infamy.IsolatedMacVlan(hport0) as ns0:
+14 -6
View File
@@ -8,26 +8,34 @@ graph "2x2" {
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"];
PC [
label="PC | { <mgmt1> mgmt1 | <data> data | <mgmt2> mgmt2 }",
pos="20,80!",
label="PC | { <mgmt1> mgmt1 | <data> data | <mgmt2> mgmt2 | <mgmt3> mgmt3 }",
pos="20,30!",
requires="controller",
];
R1 [
label="{ <mgmt> mgmt | <data> data | <link> link} | R1 \n 192.168.100.1/32 \n(lo)",
pos="250,80!",
pos="160,60!",
requires="infix",
];
R2 [
label="{ <link> link | <mgmt> mgmt | <data> data } | R2 \n 192.168.200.1/32 \n(lo)",
pos="250,30!",
pos="160,30!",
requires="infix",
];
HOST [
label="{ <link> link | <mgmt> mgmt } | HOST \n end-device",
pos="153,0!",
requires="infix",
];
PC:mgmt1 -- R1:mgmt [requires="mgmt", color="lightgray"]
PC:mgmt2 -- R2:mgmt [requires="mgmt", color="lightgray"]
PC:data -- R1:data [color="black", headlabel="192.168.10.1/24", taillabel="192.168.10.2/24", fontcolor="black"]
PC:mgmt3 -- HOST:mgmt [requires="mgmt", color="lightgray"]
PC:data -- R1:data [color="black", headlabel="192.168.10.1/24", taillabel="192.168.10.2/24", labeldistance=6, fontcolor="black"]
R1:link -- R2:link [headlabel="192.168.50.2/24", taillabel="192.168.50.1/24", labeldistance=1, fontcolor="black", color="black"]
R2:data -- HOST:link [headlabel="192.168.60.2/24", taillabel="192.168.60.1/24", labeldistance=1, fontcolor="black", color="black"]
}
+62 -37
View File
@@ -3,74 +3,99 @@
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Title: 2x2 Pages: 1 -->
<svg width="713pt" height="198pt"
viewBox="0.00 0.00 713.06 198.01" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 194.01)">
<svg width="720pt" height="306pt"
viewBox="0.00 0.00 720.00 306.08" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 302.52)">
<title>2x2</title>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-194.01 709.06,-194.01 709.06,4 -4,4"/>
<polygon fill="white" stroke="transparent" points="-4,4 -4,-302.52 717.06,-302.52 717.06,4 -4,4"/>
<!-- PC -->
<g id="node1" class="node">
<title>PC</title>
<polygon fill="none" stroke="black" points="0,-120.51 0,-189.51 91,-189.51 91,-120.51 0,-120.51"/>
<text text-anchor="middle" x="16.5" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
<polyline fill="none" stroke="black" points="33,-120.51 33,-189.51 "/>
<polygon fill="none" stroke="black" points="0,-97.51 0,-189.51 91,-189.51 91,-97.51 0,-97.51"/>
<text text-anchor="middle" x="16.5" y="-139.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">PC</text>
<polyline fill="none" stroke="black" points="33,-97.51 33,-189.51 "/>
<text text-anchor="middle" x="62" y="-174.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt1</text>
<polyline fill="none" stroke="black" points="33,-166.51 91,-166.51 "/>
<text text-anchor="middle" x="62" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="33,-143.51 91,-143.51 "/>
<text text-anchor="middle" x="62" y="-128.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt2</text>
<polyline fill="none" stroke="black" points="33,-120.51 91,-120.51 "/>
<text text-anchor="middle" x="62" y="-105.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt3</text>
</g>
<!-- R1 -->
<g id="node2" class="node">
<title>R1</title>
<polygon fill="none" stroke="black" points="490.06,-120.51 490.06,-189.51 705.06,-189.51 705.06,-120.51 490.06,-120.51"/>
<text text-anchor="middle" x="515.06" y="-174.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="490.06,-166.51 540.06,-166.51 "/>
<text text-anchor="middle" x="515.06" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="490.06,-143.51 540.06,-143.51 "/>
<text text-anchor="middle" x="515.06" y="-128.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="540.06,-120.51 540.06,-189.51 "/>
<text text-anchor="middle" x="622.56" y="-166.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 </text>
<text text-anchor="middle" x="622.56" y="-151.31" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.100.1/32 </text>
<text text-anchor="middle" x="622.56" y="-136.31" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
<polygon fill="none" stroke="black" points="498.06,-229.02 498.06,-298.02 713.06,-298.02 713.06,-229.02 498.06,-229.02"/>
<text text-anchor="middle" x="523.06" y="-282.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="498.06,-275.02 548.06,-275.02 "/>
<text text-anchor="middle" x="523.06" y="-259.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="498.06,-252.02 548.06,-252.02 "/>
<text text-anchor="middle" x="523.06" y="-236.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="548.06,-229.02 548.06,-298.02 "/>
<text text-anchor="middle" x="630.56" y="-274.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">R1 </text>
<text text-anchor="middle" x="630.56" y="-259.82" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.100.1/32 </text>
<text text-anchor="middle" x="630.56" y="-244.82" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
</g>
<!-- PC&#45;&#45;R1 -->
<g id="edge1" class="edge">
<title>PC:mgmt1&#45;&#45;R1:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-178.01C91.5,-178.01 489.56,-178.01 489.56,-178.01"/>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-178.51C91.5,-178.51 497.56,-286.52 497.56,-286.52"/>
</g>
<!-- PC&#45;&#45;R1 -->
<g id="edge3" class="edge">
<g id="edge4" class="edge">
<title>PC:data&#45;&#45;R1:data</title>
<path fill="none" stroke="black" stroke-width="2" d="M91.5,-155.01C91.5,-155.01 489.56,-155.01 489.56,-155.01"/>
<text text-anchor="middle" x="430.56" y="-158.81" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.1/24</text>
<text text-anchor="middle" x="150.5" y="-158.81" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.2/24</text>
<path fill="none" stroke="black" stroke-width="2" d="M91.5,-155.51C91.5,-155.51 497.56,-263.52 497.56,-263.52"/>
<text text-anchor="middle" x="438.49" y="-270.35" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.1/24</text>
<text text-anchor="middle" x="150.57" y="-141.29" font-family="DejaVu Serif, Book" font-size="14.00">192.168.10.2/24</text>
</g>
<!-- R2 -->
<g id="node3" class="node">
<title>R2</title>
<polygon fill="none" stroke="black" points="490.06,-0.5 490.06,-69.5 705.06,-69.5 705.06,-0.5 490.06,-0.5"/>
<text text-anchor="middle" x="515.06" y="-54.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="490.06,-46.5 540.06,-46.5 "/>
<text text-anchor="middle" x="515.06" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="490.06,-23.5 540.06,-23.5 "/>
<text text-anchor="middle" x="515.06" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="540.06,-0.5 540.06,-69.5 "/>
<text text-anchor="middle" x="622.56" y="-46.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 </text>
<text text-anchor="middle" x="622.56" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.200.1/32 </text>
<text text-anchor="middle" x="622.56" y="-16.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
<polygon fill="none" stroke="black" points="498.06,-109.01 498.06,-178.01 713.06,-178.01 713.06,-109.01 498.06,-109.01"/>
<text text-anchor="middle" x="523.06" y="-162.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="498.06,-155.01 548.06,-155.01 "/>
<text text-anchor="middle" x="523.06" y="-139.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="498.06,-132.01 548.06,-132.01 "/>
<text text-anchor="middle" x="523.06" y="-116.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">data</text>
<polyline fill="none" stroke="black" points="548.06,-109.01 548.06,-178.01 "/>
<text text-anchor="middle" x="630.56" y="-154.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">R2 </text>
<text text-anchor="middle" x="630.56" y="-139.81" font-family="DejaVu Sans Mono, Book" font-size="14.00"> 192.168.200.1/32 </text>
<text text-anchor="middle" x="630.56" y="-124.81" font-family="DejaVu Sans Mono, Book" font-size="14.00">(lo)</text>
</g>
<!-- PC&#45;&#45;R2 -->
<g id="edge2" class="edge">
<title>PC:mgmt2&#45;&#45;R2:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-132.01C91.5,-132.01 489.56,-35 489.56,-35"/>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-131.51C91.5,-131.51 497.56,-143.51 497.56,-143.51"/>
</g>
<!-- HOST -->
<g id="node4" class="node">
<title>HOST</title>
<polygon fill="none" stroke="black" points="499.05,-0.5 499.05,-46.5 656.05,-46.5 656.05,-0.5 499.05,-0.5"/>
<text text-anchor="middle" x="524.05" y="-31.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">link</text>
<polyline fill="none" stroke="black" points="499.05,-23.5 549.05,-23.5 "/>
<text text-anchor="middle" x="524.05" y="-8.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">mgmt</text>
<polyline fill="none" stroke="black" points="549.05,-0.5 549.05,-46.5 "/>
<text text-anchor="middle" x="602.55" y="-27.3" font-family="DejaVu Sans Mono, Book" font-size="14.00">HOST </text>
<text text-anchor="middle" x="602.55" y="-12.3" font-family="DejaVu Sans Mono, Book" font-size="14.00"> end&#45;device</text>
</g>
<!-- PC&#45;&#45;HOST -->
<g id="edge3" class="edge">
<title>PC:mgmt3&#45;&#45;HOST:mgmt</title>
<path fill="none" stroke="lightgray" stroke-width="2" d="M91.5,-108.51C91.5,-108.51 498.55,-11.5 498.55,-11.5"/>
</g>
<!-- R1&#45;&#45;R2 -->
<g id="edge4" class="edge">
<g id="edge5" class="edge">
<title>R1:link&#45;&#45;R2:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M514.56,-120.01C514.56,-120.01 514.56,-70 514.56,-70"/>
<text text-anchor="middle" x="518.78" y="-75.36" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.2/24</text>
<text text-anchor="middle" x="510.33" y="-107.25" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.1/24</text>
<path fill="none" stroke="black" stroke-width="2" d="M522.56,-228.52C522.56,-228.52 522.56,-178.51 522.56,-178.51"/>
<text text-anchor="middle" x="526.78" y="-183.88" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.2/24</text>
<text text-anchor="middle" x="518.33" y="-215.76" font-family="DejaVu Serif, Book" font-size="14.00">192.168.50.1/24</text>
</g>
<!-- R2&#45;&#45;HOST -->
<g id="edge6" class="edge">
<title>R2:data&#45;&#45;HOST:link</title>
<path fill="none" stroke="black" stroke-width="2" d="M522.56,-108.51C522.56,-108.51 523.55,-46.5 523.55,-46.5"/>
<text text-anchor="middle" x="527.63" y="-51.93" font-family="DejaVu Serif, Book" font-size="14.00">192.168.60.2/24</text>
<text text-anchor="middle" x="518.48" y="-95.68" font-family="DejaVu Serif, Book" font-size="14.00">192.168.60.1/24</text>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

+10
View File
@@ -114,3 +114,13 @@ def ospf_is_area_nssa(target, area_id):
return True
return False
def ospf_has_neighbors(target):
ospf = _get_ospf_status(target)
for area in ospf.get("areas", {}).get("area", []):
for interface in area.get("interfaces", {}).get("interface", []):
if interface.get("neighbors"):
return True
return False