diff --git a/test/case/ietf_routing/ospf_basic/test.adoc b/test/case/ietf_routing/ospf_basic/test.adoc index a9842a283..d6f760c0f 100644 --- a/test/case/ietf_routing/ospf_basic/test.adoc +++ b/test/case/ietf_routing/ospf_basic/test.adoc @@ -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 diff --git a/test/case/ietf_routing/ospf_basic/test.py b/test/case/ietf_routing/ospf_basic/test.py index b99d3ec10..0ebb6006c 100755 --- a/test/case/ietf_routing/ospf_basic/test.py +++ b/test/case/ietf_routing/ospf_basic/test.py @@ -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: diff --git a/test/case/ietf_routing/ospf_basic/topology.dot b/test/case/ietf_routing/ospf_basic/topology.dot index d7b6006b0..cb1841563 100644 --- a/test/case/ietf_routing/ospf_basic/topology.dot +++ b/test/case/ietf_routing/ospf_basic/topology.dot @@ -8,26 +8,34 @@ graph "2x2" { edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; PC [ - label="PC | { mgmt1 | data | mgmt2 }", - pos="20,80!", + label="PC | { mgmt1 | data | mgmt2 | mgmt3 }", + pos="20,30!", requires="controller", ]; R1 [ label="{ mgmt | data | link} | R1 \n 192.168.100.1/32 \n(lo)", - pos="250,80!", + pos="160,60!", requires="infix", ]; R2 [ label="{ link | mgmt | data } | R2 \n 192.168.200.1/32 \n(lo)", - pos="250,30!", + pos="160,30!", requires="infix", ]; - + HOST [ + label="{ link | 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"] } diff --git a/test/case/ietf_routing/ospf_basic/topology.svg b/test/case/ietf_routing/ospf_basic/topology.svg index 955a43623..258b6726c 100644 --- a/test/case/ietf_routing/ospf_basic/topology.svg +++ b/test/case/ietf_routing/ospf_basic/topology.svg @@ -3,74 +3,99 @@ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> - - + + 2x2 - + PC - -PC - + +PC + mgmt1 data mgmt2 + +mgmt3 R1 - -mgmt - -data - -link - -R1 - 192.168.100.1/32 -(lo) + +mgmt + +data + +link + +R1 + 192.168.100.1/32 +(lo) PC:mgmt1--R1:mgmt - + - + PC:data--R1:data - -192.168.10.1/24 -192.168.10.2/24 + +192.168.10.1/24 +192.168.10.2/24 R2 - -link - -mgmt - -data - -R2 - 192.168.200.1/32 -(lo) + +link + +mgmt + +data + +R2 + 192.168.200.1/32 +(lo) PC:mgmt2--R2:mgmt - + + + + +HOST + +link + +mgmt + +HOST + end-device + + + +PC:mgmt3--HOST:mgmt + - + R1:link--R2:link - -192.168.50.2/24 -192.168.50.1/24 + +192.168.50.2/24 +192.168.50.1/24 + + + +R2:data--HOST:link + +192.168.60.2/24 +192.168.60.1/24 diff --git a/test/infamy/route.py b/test/infamy/route.py index 6e948f004..46b31a3ee 100644 --- a/test/infamy/route.py +++ b/test/infamy/route.py @@ -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