mirror of
https://git.proxmox.com/git/mirror_frr
synced 2026-02-01 16:49:01 +00:00
tests: topotests for route-flap dampening
Signed-off-by: David Schweizer <dschweizer@opensourcerouting.org>
This commit is contained in:
parent
54b34709f8
commit
cd32a1a4ee
53
tests/topotests/bgp_features/exabgp.env
Normal file
53
tests/topotests/bgp_features/exabgp.env
Normal 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
|
||||
19
tests/topotests/bgp_features/peer1/exa_readpipe.py
Normal file
19
tests/topotests/bgp_features/peer1/exa_readpipe.py
Normal 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)
|
||||
12
tests/topotests/bgp_features/peer1/exabgp.cfg
Normal file
12
tests/topotests/bgp_features/peer1/exabgp.cfg
Normal 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;
|
||||
}
|
||||
}
|
||||
19
tests/topotests/bgp_features/peer2/exa_readpipe.py
Normal file
19
tests/topotests/bgp_features/peer2/exa_readpipe.py
Normal 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)
|
||||
12
tests/topotests/bgp_features/peer2/exabgp.cfg
Normal file
12
tests/topotests/bgp_features/peer2/exabgp.cfg
Normal 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;
|
||||
}
|
||||
}
|
||||
19
tests/topotests/bgp_features/peer3/exa_readpipe.py
Normal file
19
tests/topotests/bgp_features/peer3/exa_readpipe.py
Normal 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)
|
||||
12
tests/topotests/bgp_features/peer3/exabgp.cfg
Normal file
12
tests/topotests/bgp_features/peer3/exabgp.cfg
Normal 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;
|
||||
}
|
||||
}
|
||||
19
tests/topotests/bgp_features/peer4/exa_readpipe.py
Normal file
19
tests/topotests/bgp_features/peer4/exa_readpipe.py
Normal 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)
|
||||
12
tests/topotests/bgp_features/peer4/exabgp.cfg
Normal file
12
tests/topotests/bgp_features/peer4/exabgp.cfg
Normal 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;
|
||||
}
|
||||
}
|
||||
21
tests/topotests/bgp_features/r1/bgp_damp_announced.json
Normal file
21
tests/topotests/bgp_features/r1/bgp_damp_announced.json
Normal 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" } ]
|
||||
}
|
||||
}
|
||||
10
tests/topotests/bgp_features/r1/bgp_damp_setup.json
Normal file
10
tests/topotests/bgp_features/r1/bgp_damp_setup.json
Normal 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"}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
tests/topotests/bgp_features/r2/bgp_damp_announced.json
Normal file
21
tests/topotests/bgp_features/r2/bgp_damp_announced.json
Normal 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" } ]
|
||||
}
|
||||
}
|
||||
18
tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json
Normal file
18
tests/topotests/bgp_features/r2/bgp_damp_withdrawn.json
Normal 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
|
||||
}
|
||||
@ -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))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user