-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: Verify route preference (OSPF vs Static)
Fixes #784
- Loading branch information
Showing
7 changed files
with
383 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
=== Route preference: OSPF vs Static | ||
==== Description | ||
This test configures a device with both an OSPF-acquired route on a dedicated | ||
interface and a static route to the same destination on another interface. | ||
The static route has a higher preference value than OSPF. | ||
|
||
Initially, the device should prefer the OSPF route; if the OSPF route | ||
becomes unavailable, the static route should take over. | ||
|
||
==== Topology | ||
ifdef::topdoc[] | ||
image::../../test/case/ietf_routing/route_pref_ospf/topology.svg[Route preference: OSPF vs Static topology] | ||
endif::topdoc[] | ||
ifndef::topdoc[] | ||
ifdef::testgroup[] | ||
image::route_pref_ospf/topology.svg[Route preference: OSPF vs Static topology] | ||
endif::testgroup[] | ||
ifndef::testgroup[] | ||
image::topology.svg[Route preference: OSPF vs Static topology] | ||
endif::testgroup[] | ||
endif::topdoc[] | ||
==== Test sequence | ||
. Set up topology and attach to target DUTs | ||
. Set up TPMR between R1ospf and R2ospf | ||
. Configure targets | ||
. Set up persistent MacVlan namespaces | ||
. Wait for OSPF and static routes | ||
. Verify connectivity from PC:data1 to PC:data2 via OSPF | ||
. Simulate OSPF route loss by blocking OSPF interface | ||
. Verify connectivity via static route after OSPF failover | ||
|
||
|
||
<<< | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,199 @@ | ||
#!/usr/bin/env python3 | ||
""" | ||
Route preference: OSPF vs Static | ||
This test configures a device with both an OSPF-acquired route on a | ||
dedicated interface and a static route to the same destination on | ||
another interface. The static route has a higher preference value than | ||
OSPF. | ||
Initially, the device should prefer the OSPF route; if the OSPF route | ||
becomes unavailable, the static route should take over. | ||
""" | ||
|
||
import infamy | ||
import infamy.route as route | ||
from infamy.util import until, parallel | ||
from infamy.netns import TPMR | ||
|
||
|
||
def configure_interface(name, ip, prefix_length, forwarding=True): | ||
return { | ||
"name": name, | ||
"enabled": True, | ||
"ipv4": { | ||
"forwarding": forwarding, | ||
"address": [{"ip": ip, "prefix-length": prefix_length}] | ||
} | ||
} | ||
|
||
def config_target1(target, data, link, ospf): | ||
# Use put_config_dicts for setting multiple module configurations | ||
target.put_config_dicts({ | ||
"ietf-interfaces": { | ||
"interfaces": { | ||
"interface": [ | ||
configure_interface(data, "192.168.10.1", 24), | ||
configure_interface(link, "192.168.50.1", 24), | ||
configure_interface(ospf, "192.168.60.1", 24) | ||
] | ||
} | ||
}, | ||
"ietf-routing": { | ||
"routing": { | ||
"control-plane-protocols": { | ||
"control-plane-protocol": [ | ||
{ | ||
"type": "infix-routing:ospfv2", | ||
"name": "ospf-default", | ||
"ospf": { | ||
"redistribute": { | ||
"redistribute": [{"protocol": "connected"}] | ||
}, | ||
"areas": { | ||
"area": [{ | ||
"area-id": "0.0.0.0", | ||
"interfaces": { | ||
"interface": [{ | ||
"name": ospf, | ||
"hello-interval": 1, | ||
"dead-interval": 3 | ||
}] | ||
} | ||
}] | ||
} | ||
} | ||
}, | ||
{ | ||
"type": "infix-routing:static", | ||
"name": "dot20", | ||
"static-routes": { | ||
"ipv4": { | ||
"route": [{ | ||
"destination-prefix": "192.168.20.0/24", | ||
"next-hop": {"next-hop-address": "192.168.50.2"}, | ||
"route-preference": 120 | ||
}] | ||
} | ||
} | ||
} | ||
] | ||
} | ||
} | ||
} | ||
}) | ||
|
||
def config_target2(target, data, link, ospf): | ||
# Use put_config_dicts for setting multiple module configurations | ||
target.put_config_dicts({ | ||
"ietf-interfaces": { | ||
"interfaces": { | ||
"interface": [ | ||
configure_interface(data, "192.168.20.2", 24), | ||
configure_interface(link, "192.168.50.2", 24), | ||
configure_interface(ospf, "192.168.60.2", 24) | ||
] | ||
} | ||
}, | ||
"ietf-routing": { | ||
"routing": { | ||
"control-plane-protocols": { | ||
"control-plane-protocol": [ | ||
{ | ||
"type": "infix-routing:ospfv2", | ||
"name": "ospf-default", | ||
"ospf": { | ||
"redistribute": { | ||
"redistribute": [{"protocol": "connected"}] | ||
}, | ||
"areas": { | ||
"area": [{ | ||
"area-id": "0.0.0.0", | ||
"interfaces": { | ||
"interface": [{ | ||
"name": ospf, | ||
"hello-interval": 1, | ||
"dead-interval": 3 | ||
}] | ||
} | ||
}] | ||
} | ||
} | ||
}, | ||
{ | ||
"type": "infix-routing:static", | ||
"name": "default", | ||
"static-routes": { | ||
"ipv4": { | ||
"route": [{ | ||
"destination-prefix": "0.0.0.0/0", | ||
"next-hop": {"next-hop-address": "192.168.50.1"} | ||
}] | ||
} | ||
} | ||
} | ||
] | ||
} | ||
} | ||
} | ||
}) | ||
|
||
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") | ||
|
||
with test.step("Set up TPMR between R1ospf and R2ospf"): | ||
ospf_breaker = TPMR(env.ltop.xlate("PC", "R1_ospf")[1], env.ltop.xlate("PC", "R2_ospf")[1]).start() | ||
|
||
with test.step("Configure targets"): | ||
_, R1data = env.ltop.xlate("R1", "data") | ||
_, R1link = env.ltop.xlate("R1", "link") | ||
_, R1ospf = env.ltop.xlate("R1", "ospf") | ||
_, R2data = env.ltop.xlate("R2", "data") | ||
_, R2link = env.ltop.xlate("R2", "link") | ||
_, R2ospf = env.ltop.xlate("R2", "ospf") | ||
|
||
parallel(config_target1(R1, R1data, R1link, R1ospf), config_target2(R2, R2data, R2link, R2ospf)) | ||
|
||
with test.step("Set up persistent MacVlan namespaces"): | ||
_, hport_data1 = env.ltop.xlate("PC", "data1") | ||
_, hport_data2 = env.ltop.xlate("PC", "data2") | ||
|
||
ns1 = infamy.IsolatedMacVlan(hport_data1).start() | ||
ns1.addip("192.168.10.11", prefix_length=24) | ||
ns1.addroute("default", "192.168.10.1") | ||
|
||
ns2 = infamy.IsolatedMacVlan(hport_data2).start() | ||
ns2.addip("192.168.20.22", prefix_length=24) | ||
ns2.addroute("default", "192.168.20.2") | ||
|
||
with test.step("Wait for OSPF and static routes"): | ||
print("Waiting for OSPF and static routes...") | ||
until(lambda: route.ipv4_route_exist(R1, "192.168.20.0/24", proto="ietf-ospf:ospfv2"), attempts=200) | ||
until(lambda: route.ipv4_route_exist(R1, "192.168.20.0/24", proto="ietf-routing:static"), attempts=200) | ||
|
||
with test.step("Verify connectivity from PC:data1 to PC:data2 via OSPF"): | ||
ns1.must_reach("192.168.20.22") | ||
|
||
ospf_route_active = route.ipv4_route_exist(R1, "192.168.20.0/24", proto="ietf-ospf:ospfv2", active_check=True) | ||
assert ospf_route_active, "OSPF route should be preferred when available." | ||
|
||
hops = [row[1] for row in ns1.traceroute("192.168.20.22")] | ||
assert "192.168.60.2" in hops, f"Path does not use expected OSPF route: {hops}" | ||
|
||
with test.step("Simulate OSPF route loss by blocking OSPF interface"): | ||
ospf_breaker.block() | ||
until(lambda: not route.ipv4_route_exist(R1, "192.168.20.0/24", proto="ietf-ospf:ospfv2"), attempts=200) | ||
|
||
with test.step("Verify connectivity via static route after OSPF failover"): | ||
ns1.must_reach("192.168.20.22") | ||
|
||
static_route_active = route.ipv4_route_exist(R1, "192.168.20.0/24", proto="ietf-routing:static", active_check=True) | ||
assert static_route_active, "Static route should be preferred when OSPF route is unavailable." | ||
|
||
hops = [row[1] for row in ns1.traceroute("192.168.20.22")] | ||
assert "192.168.50.2" in hops, f"Path does not use expected static route: {hops}" | ||
|
||
test.succeed() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
graph "route-preference" { | ||
layout="neato"; | ||
overlap="false"; | ||
esep="+20"; | ||
size=10 | ||
|
||
node [shape=record, fontname="DejaVu Sans Mono, Book"]; | ||
edge [color="cornflowerblue", penwidth="2", fontname="DejaVu Serif, Book"]; | ||
|
||
PC | ||
[ | ||
label="PC | { <mgmt1> mgmt1 | <data1> data1 | <> \n\n | <R1_ospf> R1_ospf | <R2_ospf> R2_ospf | <> \n\n | <data2> data2 | <mgmt2> mgmt2 }", | ||
pos="20,50!", | ||
kind="controller", | ||
]; | ||
|
||
R1 | ||
[ | ||
label="{ <mgmt> mgmt | <data> data | <ospf> ospf | <link> link } | R1", | ||
pos="70,58!", | ||
kind="infix", | ||
]; | ||
|
||
R2 | ||
[ | ||
label="{ <link> link | <ospf> ospf | <data> data | <mgmt> mgmt } | R2", | ||
pos="70,42!", | ||
kind="infix", | ||
]; | ||
|
||
PC:mgmt1 -- R1:mgmt [kind=mgmt, color="lightgray"] | ||
PC:mgmt2 -- R2:mgmt [kind=mgmt, color="lightgray"] | ||
|
||
PC:data1 -- R1:data [color="black", headlabel="192.168.10.1/24", taillabel="192.168.10.11/24", fontcolor="black"] | ||
PC:data2 -- R2:data [color="black", headlabel="192.168.20.2/24", taillabel="192.168.10.22/24", fontcolor="black"] | ||
|
||
R1:link -- R2:link [headlabel="192.168.50.2/24", taillabel="192.168.50.1/24", labeldistance=1, fontcolor="black", color="black"] | ||
|
||
R1:ospf -- PC:R1_ospf [color="lightgreen", taillabel="192.168.60.1/24"] | ||
R2:ospf -- PC:R2_ospf [color="lightgreen", taillabel="192.168.60.2/24"] | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.