tests: topotests for route-flap dampening

Signed-off-by: David Schweizer <dschweizer@opensourcerouting.org>
This commit is contained in:
David Schweizer 2020-11-02 16:30:04 +01:00
parent 54b34709f8
commit cd32a1a4ee
No known key found for this signature in database
GPG Key ID: A07D97BEEE79EF7F
14 changed files with 918 additions and 0 deletions

View File

@ -0,0 +1,53 @@
[exabgp.api]
encoder = text
highres = false
respawn = false
socket = ''
[exabgp.bgp]
openwait = 60
[exabgp.cache]
attributes = true
nexthops = true
[exabgp.daemon]
daemonize = true
pid = '/var/run/exabgp/exabgp.pid'
user = 'exabgp'
[exabgp.log]
all = false
configuration = true
daemon = true
destination = '/var/log/exabgp.log'
enable = true
level = INFO
message = false
network = true
packets = false
parser = false
processes = true
reactor = true
rib = false
routes = false
short = false
timers = false
[exabgp.pdb]
enable = false
[exabgp.profile]
enable = false
file = ''
[exabgp.reactor]
speed = 1.0
[exabgp.tcp]
acl = false
bind = ''
delay = 0
once = false
port = 179

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python
"Helper script to read api commands from a pipe and feed them to ExaBGP"
import sys
if len(sys.argv) != 2:
sys.exit(1)
fifo = sys.argv[1]
while True:
pipe = open(fifo, 'r')
with pipe:
line = pipe.readline().strip()
if line != "":
sys.stdout.write("{}\n".format(line))
sys.stdout.flush()
pipe.close()
sys.exit(0)

View File

@ -0,0 +1,12 @@
group exabgp {
process announce-routes {
run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in";
encoder text;
}
neighbor 192.168.101.1 {
router-id 192.168.101.3;
local-address 192.168.101.3;
local-as 65403;
peer-as 65000;
}
}

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python
"Helper script to read api commands from a pipe and feed them to ExaBGP"
import sys
if len(sys.argv) != 2:
sys.exit(1)
fifo = sys.argv[1]
while True:
pipe = open(fifo, 'r')
with pipe:
line = pipe.readline().strip()
if line != "":
sys.stdout.write("{}\n".format(line))
sys.stdout.flush()
pipe.close()
sys.exit(0)

View File

@ -0,0 +1,12 @@
group exabgp {
process announce-routes {
run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in";
encoder text;
}
neighbor 192.168.101.1 {
router-id 192.168.101.4;
local-address 192.168.101.4;
local-as 65404;
peer-as 65000;
}
}

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python
"Helper script to read api commands from a pipe and feed them to ExaBGP"
import sys
if len(sys.argv) != 2:
sys.exit(1)
fifo = sys.argv[1]
while True:
pipe = open(fifo, 'r')
with pipe:
line = pipe.readline().strip()
if line != "":
sys.stdout.write("{}\n".format(line))
sys.stdout.flush()
pipe.close()
sys.exit(0)

View File

@ -0,0 +1,12 @@
group exabgp {
process announce-routes {
run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in";
encoder text;
}
neighbor 192.168.101.1 {
router-id 192.168.101.5;
local-address 192.168.101.5;
local-as 65405;
peer-as 65000;
}
}

View File

@ -0,0 +1,19 @@
#!/usr/bin/env python
"Helper script to read api commands from a pipe and feed them to ExaBGP"
import sys
if len(sys.argv) != 2:
sys.exit(1)
fifo = sys.argv[1]
while True:
pipe = open(fifo, 'r')
with pipe:
line = pipe.readline().strip()
if line != "":
sys.stdout.write("{}\n".format(line))
sys.stdout.flush()
pipe.close()
sys.exit(0)

View File

@ -0,0 +1,12 @@
group exabgp {
process announce-routes {
run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in";
encoder text;
}
neighbor 192.168.101.1 {
router-id 192.168.101.6;
local-address 192.168.101.6;
local-as 65406;
peer-as 65000;
}
}

View File

@ -0,0 +1,21 @@
{
"localAS":65000,
"routes":{
"192.168.31.0/24": [ { "valid":true, "network":"192.168.31.0\/24", "peerId":"192.168.101.3" } ],
"192.168.32.0/24": [ { "valid":true, "network":"192.168.32.0\/24", "peerId":"192.168.101.3" } ],
"192.168.33.0/24": [ { "valid":true, "network":"192.168.33.0\/24", "peerId":"192.168.101.3" } ],
"192.168.34.0/24": [ { "valid":true, "network":"192.168.34.0\/24", "peerId":"192.168.101.3" } ],
"192.168.41.0/24": [ { "valid":true, "network":"192.168.41.0\/24", "peerId":"192.168.101.4" } ],
"192.168.42.0/24": [ { "valid":true, "network":"192.168.42.0\/24", "peerId":"192.168.101.4" } ],
"192.168.43.0/24": [ { "valid":true, "network":"192.168.43.0\/24", "peerId":"192.168.101.4" } ],
"192.168.44.0/24": [ { "valid":true, "network":"192.168.44.0\/24", "peerId":"192.168.101.4" } ],
"192.168.51.0/24": [ { "valid":true, "network":"192.168.51.0\/24", "peerId":"192.168.101.5" } ],
"192.168.52.0/24": [ { "valid":true, "network":"192.168.52.0\/24", "peerId":"192.168.101.5" } ],
"192.168.53.0/24": [ { "valid":true, "network":"192.168.53.0\/24", "peerId":"192.168.101.5" } ],
"192.168.54.0/24": [ { "valid":true, "network":"192.168.54.0\/24", "peerId":"192.168.101.5" } ],
"192.168.61.0/24": [ { "valid":true, "network":"192.168.61.0\/24", "peerId":"192.168.101.6" } ],
"192.168.62.0/24": [ { "valid":true, "network":"192.168.62.0\/24", "peerId":"192.168.101.6" } ],
"192.168.63.0/24": [ { "valid":true, "network":"192.168.63.0\/24", "peerId":"192.168.101.6" } ],
"192.168.64.0/24": [ { "valid":true, "network":"192.168.64.0\/24", "peerId":"192.168.101.6" } ]
}
}

View File

@ -0,0 +1,10 @@
{
"ipv4Unicast":{
"peers":{
"192.168.101.3":{"remoteAs":65403, "state":"Established"},
"192.168.101.4":{"remoteAs":65404, "state":"Established"},
"192.168.101.5":{"remoteAs":65405, "state":"Established"},
"192.168.101.6":{"remoteAs":65406, "state":"Established"}
}
}
}

View File

@ -0,0 +1,21 @@
{
"localAS":65000,
"routes":{
"192.168.31.0/24": [ { "network":"192.168.31.0\/24", "peerId":"192.168.0.1" } ],
"192.168.32.0/24": [ { "network":"192.168.32.0\/24", "peerId":"192.168.0.1" } ],
"192.168.33.0/24": [ { "network":"192.168.33.0\/24", "peerId":"192.168.0.1" } ],
"192.168.34.0/24": [ { "network":"192.168.34.0\/24", "peerId":"192.168.0.1" } ],
"192.168.41.0/24": [ { "network":"192.168.41.0\/24", "peerId":"192.168.0.1" } ],
"192.168.42.0/24": [ { "network":"192.168.42.0\/24", "peerId":"192.168.0.1" } ],
"192.168.43.0/24": [ { "network":"192.168.43.0\/24", "peerId":"192.168.0.1" } ],
"192.168.44.0/24": [ { "network":"192.168.44.0\/24", "peerId":"192.168.0.1" } ],
"192.168.51.0/24": [ { "network":"192.168.51.0\/24", "peerId":"192.168.0.1" } ],
"192.168.52.0/24": [ { "network":"192.168.52.0\/24", "peerId":"192.168.0.1" } ],
"192.168.53.0/24": [ { "network":"192.168.53.0\/24", "peerId":"192.168.0.1" } ],
"192.168.54.0/24": [ { "network":"192.168.54.0\/24", "peerId":"192.168.0.1" } ],
"192.168.61.0/24": [ { "network":"192.168.61.0\/24", "peerId":"192.168.0.1" } ],
"192.168.62.0/24": [ { "network":"192.168.62.0\/24", "peerId":"192.168.0.1" } ],
"192.168.63.0/24": [ { "network":"192.168.63.0\/24", "peerId":"192.168.0.1" } ],
"192.168.64.0/24": [ { "network":"192.168.64.0\/24", "peerId":"192.168.0.1" } ]
}
}

View File

@ -0,0 +1,18 @@
{
"192.168.31.0/24": null,
"192.168.32.0/24": null,
"192.168.33.0/24": null,
"192.168.34.0/24": null,
"192.168.41.0/24": null,
"192.168.42.0/24": null,
"192.168.43.0/24": null,
"192.168.44.0/24": null,
"192.168.51.0/24": null,
"192.168.52.0/24": null,
"192.168.53.0/24": null,
"192.168.54.0/24": null,
"192.168.61.0/24": null,
"192.168.62.0/24": null,
"192.168.63.0/24": null,
"192.168.64.0/24": null
}

View File

@ -33,6 +33,7 @@ import sys
import pytest
import re
import time
from time import sleep
# Save the Current Working Directory to find configuration files.
CWD = os.path.dirname(os.path.realpath(__file__))
@ -64,6 +65,14 @@ class BGPFeaturesTopo1(Topo):
for rtrNum in range(1, 6):
tgen.add_router("r{}".format(rtrNum))
# create ExaBGP peers
for peer_num in range(1, 5):
tgen.add_exabgp_peer(
"peer{}".format(peer_num),
ip="192.168.101.{}".format(peer_num + 2),
defaultRoute="via 192.168.101.1",
)
# Setup Switches and connections
for swNum in range(1, 11):
tgen.add_switch("sw{}".format(swNum))
@ -89,6 +98,12 @@ class BGPFeaturesTopo1(Topo):
tgen.gears["r2"].add_link(tgen.gears["sw5"])
tgen.gears["r5"].add_link(tgen.gears["sw5"])
# Add ExaBGP peers to sw4
tgen.gears["peer1"].add_link(tgen.gears["sw4"])
tgen.gears["peer2"].add_link(tgen.gears["sw4"])
tgen.gears["peer3"].add_link(tgen.gears["sw4"])
tgen.gears["peer4"].add_link(tgen.gears["sw4"])
#####################################################
#
@ -1093,6 +1108,662 @@ def test_bgp_delayopen_dual():
# end test_bgp_delayopen_dual
def test_bgp_dampening_setup():
"BGP route-flap dampening test setup"
# This test starts four ExaBGP peers, adds them as neighbors to the
# configuration of router r1 and checks if connections get established.
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("Starting BGP route-flap dampening test setup")
# Start ExaBGP peers connected to r1 via switch 4
logger.info("Starting ExaBGP peers")
for peer_num in range(1, 5):
logger.info("Creating named pipe for ExaBGP peer peer{}".format(peer_num))
fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num)
if os.path.exists(fifo_in):
os.remove(fifo_in)
os.mkfifo(fifo_in, 0o777)
logger.info("Starting ExaBGP on peer peer{}".format(peer_num))
peer = tgen.gears["peer{}".format(peer_num)]
peer_dir = os.path.join(CWD, "peer{}".format(peer_num))
env_file = os.path.join(CWD, "exabgp.env")
peer.start(peer_dir, env_file)
# Add ExaBGP peers to configuration of router r2
logger.info("Adding ExaBGP peers as neighbors to configuration of router r2")
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 remote-as 65403"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-in"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 route-map testmap-out"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 remote-as 65404"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-in"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.4 route-map testmap-out"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.5 remote-as 65405"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-in"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 route-map testmap-out"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.6 remote-as 65406"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-in"'
)
tgen.net["r1"].cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.6 route-map testmap-out"'
)
# Check if exabgp peers are up and running
logger.info("Checking for established connections to ExaBGP peers on router r1")
router = tgen.gears["r1"]
reffile = os.path.join(CWD, "r1/bgp_damp_setup.json")
expected = json.loads(open(reffile).read())
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp summary json", expected
)
_, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
assertmsg = (
"BGP session on r1 did not establish connections with one ore more ExaBGP peers"
)
assert res is None, assertmsg
# end test_bgp_dampening_setup
def test_bgp_dampening_route_announce():
"Test of BGP route-flap dampening route announcement"
# This test checks if the four ExaBGP peers can announce routes to router
# r1 and if these routes get forwarded to router r2.
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("Starting test of BGP route-flap dampening route announcement")
# Announce routes on exabgp peers to r2
logger.info("Announcing routes on ExaBGP peers to r1")
for prefix_iter in range(1, 5):
for peer_num in range(1, 5):
pipe = open("/run/exabgp_peer{}.in".format(peer_num), "w")
with pipe:
pipe.write(
"announce route 192.168.{}{}.0/24 next-hop 192.168.101.{}\n".format(
(peer_num + 2), prefix_iter, (peer_num + 2)
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
# Check if routes announced by ExaBGP peers are present in RIB of router r1
logger.info(
"Checking if routes announced by ExaBGP peers are present in RIB of router r1"
)
router = tgen.gears["r1"]
reffile = os.path.join(CWD, "r1/bgp_damp_announced.json")
expected = json.loads(open(reffile).read())
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp json", expected
)
_, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
assertmsg = (
"BGP session on router r1 did not receive routes announced by ExaBGP peers"
)
assert res is None, assertmsg
# Check if routes announced by ExaBGP peers to router r1 have been forwarded
# and are now present in RIB of router r2
logger.info(
"Checking if forwarded routes announced by ExaBGP peers are present in RIB of router r2"
)
router = tgen.gears["r2"]
reffile = os.path.join(CWD, "r2/bgp_damp_announced.json")
expected = json.loads(open(reffile).read())
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp json", expected
)
_, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
assertmsg = "BGP session on router r2 did not receive routes announced by ExaBGP peers forwarded by router r1"
assert res is None, assertmsg
# end test_bgp_dampening_route_announce
def test_bgp_dampening_disabled():
"Test of BGP route-flapping with dampening disabled"
# This test verifies that flapped routes do not get withdrawn from the RIB
# of router r1 if dampening is disabled.
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("Starting test of BGP route-flapping with dampening disabled")
# Flapping routes on ExaBGP peer peer1
logger.info(
"Flapping routes on ExaBGP peer peer1 with route-flap dampening disabled"
)
for _ in range(1, 5):
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer1.in", "w")
with pipe:
pipe.write(
"withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
sleep(1) # Give the BGP session on router r1 time to process routes
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer1.in", "w")
with pipe:
pipe.write(
"announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
# Verify flapped routes are still present in RIB of router r1
logger.info(
"Verifying that the flapped routes are still present in RIB of router r1"
)
router = tgen.gears["r1"]
reffile = os.path.join(CWD, "r1/bgp_damp_announced.json")
expected = json.loads(open(reffile).read())
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp json", expected
)
_, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
assertmsg = "BGP session on router r1 removed flapped routes despite route-flap dampening being disabled"
assert res is None, assertmsg
# end test_bgp_dampening_disabled
def test_bgp_dampening_config():
"Test of BGP route-flap dampening configuration"
# This test adds peer-group group1 with peers peer1 and peer2 to the
# configuration of router r1, sets up dampening configurations with
# different profiles and verifies the configured dampening parameters.
tgen = get_topogen()
r_1 = tgen.net["r1"]
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("Starting test of BGP route-flap dampening configuration")
# Add peer-group group1 with peers peer1 and peer2
logger.info(
"Creating peer-group group1 and adding ExaBGP peers peer1 and peer2 to it"
)
r_1.cmd('vtysh -c "conf t" -c "router bgp 65000" -c "neighbor group1 peer-group"')
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.3 peer-group group1"'
) # peer1
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.4 peer-group group1"'
) # peer2
# Enable different dampening profiles for peer1, peer3, group1 and global
# configuration
logger.info(
"Enabling different dampening profiles for peer1, peer3, group1 and global configuration"
)
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "bgp dampening 30 300 900 90"'
)
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor group1 dampening 20 200 600 60"'
)
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.3 dampening 10 100 300 30"'
) # peer1
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "neighbor 192.168.101.5 dampening 10 100 300 30"'
) # peer3
# Verify route-flap dampening configuration
logger.info("Verifying route-flap dampening configuration on router r1")
vtyout = r_1.cmd('vtysh -c "show running-config"')
assertmsg = "BGP Session on r1 does not show enabled global route-flap dampening in running configuration"
assert re.search("bgp dampening 30 300 900 90", vtyout), assertmsg
assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer-group group1 in running configuration"
assert re.search("neighbor group1 dampening 20 200 600 60", vtyout), assertmsg
assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer1 in running configuration"
assert re.search(
"neighbor 192.168.101.3 dampening 10 100 300 30", vtyout
), assertmsg
assertmsg = "BGP Session on r1 does not show route-flap dampening enabled for peer peer3 in running configuration"
assert re.search(
"neighbor 192.168.101.5 dampening 10 100 300 30", vtyout
), assertmsg
# end test_bgp_dampening_config
def test_bgp_dampening_profile_peer_over_group():
"Test of BGP route-flap dampening profile preferences: peer over group"
# This test verifies that the dampening profile of a peer takes precedence
# over the dampening profile of its peer-group by flapping the peers routes
# until dampened and comparing the reuse times to the one specified in the
# dampening configuration.
tgen = get_topogen()
r_1 = tgen.net["r1"]
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info(
"Starting test of BGP route-flap dampening profile preferences: peer over group"
)
# Flapping routes on ExaBGP peer peer1
logger.info(
"Flapping routes on ExaBGP peer peer1 with route-flap dampening enabled"
)
for _ in range(1, 5):
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer1.in", "w")
with pipe:
pipe.write(
"withdraw route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
sleep(1) # Give the BGP session on router r1 time to process routes
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer1.in", "w")
with pipe:
pipe.write(
"announce route 192.168.3{}.0/24 next-hop 192.168.101.3\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
# Check damped paths on r1 for routes of peer1 witn peer profile
logger.info(
"Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer1"
)
sleep(5) # Wait 5 seconds for paths to show up in dampened-paths list
vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
routes = re.findall(r"\*d 192\.168\.3\d\.0\/24.*", vtyout)
assertmsg = (
"BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer1"
)
assert len(routes) == 4, assertmsg
assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer1"
for route in routes:
assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
35 > int(route.split()[3].split(":")[1]) > 25
), assertmsg # minutes of reuse time
# end test_bgp_dampening_profile_peer_over_group
def test_bgp_dampening_profile_group_over_global():
"Test of BGP route-flap dampening profile preferences: group over global"
# This test verifies that the dampening profile of a peer-group takes
# precedence over the global dampening profile by flapping the routes of a
# peer-group member until dampened and comparing the reuse times to the one
# specified in the dampening configuration.
tgen = get_topogen()
r_1 = tgen.net["r1"]
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info(
"Starting test of BGP route-flap dampening profile preferences: group over global"
)
# Flapping routes on ExaBGP peer peer2
logger.info(
"Flapping routes on ExaBGP peer peer2 with route-flap dampening enabled"
)
for _ in range(1, 5):
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer2.in", "w")
with pipe:
pipe.write(
"withdraw route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
sleep(1) # Give the BGP session on router r1 time to process routes
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer2.in", "w")
with pipe:
pipe.write(
"announce route 192.168.4{}.0/24 next-hop 192.168.101.4\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
# Check damped paths on r1 for routes of peer2 witn group profile
logger.info(
"Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer2"
)
sleep(5) # wait 5 seconds for paths to shop up in damp list
vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
routes = re.findall(r"\*d 192\.168\.4\d\.0\/24.*", vtyout)
assertmsg = (
"BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer2"
)
assert len(routes) == 4, assertmsg
assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer2"
for route in routes:
assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
65 > int(route.split()[3].split(":")[1]) > 55
), assertmsg # minutes of reuse time
# end test_bgp_dampening_profile_group_over_global
def test_bgp_dampening_profile_peer_over_global():
"Test of BGP route-flap dampening profile preferences: peer over global"
# This test verifies that the dampening profile of a peer takes precedence
# over the global dampening profile by flapping the routes of the peer until
# dampened and comparing the reuse times to the one specified in the
# dampening configuration.
tgen = get_topogen()
r_1 = tgen.net["r1"]
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info(
"Starting test of BGP route-flap dampening profile preferences: peer over global"
)
# Flapping routes on ExaBGP peer peer3
logger.info(
"Flapping routes on ExaBGP peer peer3 with route-flap dampening enabled"
)
for _ in range(1, 5):
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer3.in", "w")
with pipe:
pipe.write(
"withdraw route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
sleep(1) # Give the BGP session on router r1 time to process routes
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer3.in", "w")
with pipe:
pipe.write(
"announce route 192.168.5{}.0/24 next-hop 192.168.101.5\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
# Check damped paths on r1 for routes of peer3 witn peer profile
logger.info(
"Checking if router r1 used the correct dampening profile on routes flapped by ExaBGP peer peer3"
)
sleep(5) # wait 5 seconds for paths to shop up in damp list
vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
routes = re.findall(r"\*d 192\.168\.5\d\.0\/24.*", vtyout)
assertmsg = (
"BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer3"
)
assert len(routes) == 4, assertmsg
assertmsg = "BGP session on router r1 used wrong dampening profile for a route flapped by ExaBGP peer peer3"
for route in routes:
assert (int(route.split()[3].split(":")[0]) == 0) and ( # hours of reuse time
35 > int(route.split()[3].split(":")[1]) > 25
), assertmsg # minutes of reuse time
# end test_bgp_dampening_profile_peer_over_global
def test_bgp_dampening_profile_global():
"Test of BGP route-flap dampening global profile"
# This test verifies the application of the global dampening profile by
# flapping the routes of a peer until dampened and comparing the reuse times
# to the one specified in the dampening configuration.
tgen = get_topogen()
r_1 = tgen.net["r1"]
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("Starting test of BGP route-flap dampening global profile")
# Flapping routes on ExaBGP peer peer4
logger.info(
"Flapping routes on ExaBGP peer peer4 with route-flap dampening enabled"
)
for _ in range(1, 5):
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer4.in", "w")
with pipe:
pipe.write(
"withdraw route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
sleep(1) # Give the BGP session on router r1 time to process routes
for prefix_iter in range(1, 5):
pipe = open("/run/exabgp_peer4.in", "w")
with pipe:
pipe.write(
"announce route 192.168.6{}.0/24 next-hop 192.168.101.6\n".format(
prefix_iter
)
)
pipe.close()
sleep(0.1) # ExaBGP API command processing delay
# Check damped paths on r1 for routes of peer4 witn global profile
logger.info(
"Checking if router r1 used the global dampening profile on routes flapped by ExaBGP peer peer4"
)
sleep(5) # wait 5 seconds for paths to shop up in damp list
vtyout = r_1.cmd('vtysh -c "show ip bgp dampening dampened-paths"')
routes = re.findall(r"\*d 192\.168\.6\d\.0\/24.*", vtyout)
assertmsg = (
"BGP session on router r1 did not dampen routes flapped by ExaBGP peer peer4"
)
assert len(routes) == 4, assertmsg
assertmsg = "BGP session on router r1 did not use the global dampening profile for a route flapped by ExaBGP peer peer4"
for route in routes:
assert (int(route.split()[3].split(":")[0]) == 1) and ( # hours of reuse time
35 > int(route.split()[3].split(":")[1]) > 25
), assertmsg # minutes of reuse time
# end test_bgp_dampening_profile_global
def test_bgp_dampening_withdaw():
"Test BGP route-flap dampening route withdraw"
# This test verifies that the withrawl of dampened routes from the RIB of
# router r1 was propagated to router r2.
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("Starting test of BGP route-flap dampening route withdraw")
# Check if routes dampened on router r1 have been withdrawn from the RIB on
# router r2
logger.info(
"Checking if routes dampened on router r1 have been withdrawn of RIB on router r2"
)
reffile = os.path.join(CWD, "r2/bgp_damp_withdrawn.json")
expected = json.loads(open(reffile).read())
test_func = functools.partial(
topotest.router_json_cmp, tgen.gears["r2"], "show ip bgp json", expected
)
_, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
assertmsg = "BGP session on router r2 did not receive withdraw of routes dampened on router r1"
assert res is None, assertmsg
# end test_bgp_dampening_withdaw
def test_bgp_dampening_cleanup():
"BGP route-flap dampening test cleanup"
# This test cleans up after other tests associated with route-flap dampening
# by disabling all dampening configurations, removing added peers and
# peer-groups from the configuration on router r1, and shutting down ExaBGP
# peers peer1, peer2 and peer3.
tgen = get_topogen()
r_1 = tgen.net["r1"]
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
logger.info("Starting BGP route-flap dampening test cleanup")
# Disable all dampening configurations
logger.info("Disabling all dampening configurations")
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no bgp dampening"'
)
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor group1 dampening"'
)
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.3 dampening"'
)
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "address-family ipv4 unicast" -c "no neighbor 192.168.101.5 dampening"'
)
# Remove ExaBGP peers from configuration of router r1
logger.info("Removing ExaBGP peers from configuration of router r1")
for router_num in range(3, 7):
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.{}"'.format(
router_num
)
)
# Remove peer-group group1 from configuration of router r1
logger.info("Removing peer-group group1 peers from configuration of router r1")
r_1.cmd(
'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor group1 peer-group"'
)
# Stop ExaBGP peers and remove associated named pipes
logger.info("Stopping ExaBGP peers and removing associated named pipes")
for peer_num in range(1, 5):
logger.info("Terminating ExaBGP on peer peer{}".format(peer_num))
peer = tgen.gears["peer{}".format(peer_num)]
logger.info("Removing named pipe of ExaBGP peer peer{}".format(peer_num))
fifo_in = "/var/run/exabgp_peer{}.in".format(peer_num)
peer.stop()
if os.path.exists(fifo_in):
os.remove(fifo_in)
# end test_bgp_dampening_cleanup
def test_bgp_dampening_aftermath():
"BGP route-flap dampening aftermath test"
# This test verifies routers r1 and r2 not being affected by the route-flap
# dampening test series.
tgen = get_topogen()
# Skip if previous fatal error condition is raised
if tgen.routers_have_failure():
pytest.skip(tgen.errors)
# Check BGP Summary on routers r1 and r2
for rtr_num in [1, 2]:
logger.info(
"Checking if BGP router on r{} remains unaffected by route-flap dampening tests".format(
rtr_num
)
)
router = tgen.gears["r{}".format(rtr_num)]
reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtr_num))
expected = json.loads(open(reffile).read())
test_func = functools.partial(
topotest.router_json_cmp, router, "show ip bgp json", expected
)
_, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
assertmsg = "BGP routes on router r{} are wrong after route-flap dampening tests".format(
rtr_num
)
assert res is None, assertmsg
# end test_bgp_dampening_aftermath
if __name__ == "__main__":
args = ["-s"] + sys.argv[1:]
sys.exit(pytest.main(args))