From 39bea45c190af068c5f684d63a1e57ecc06e5e81 Mon Sep 17 00:00:00 2001 From: Rafael Zalamena Date: Tue, 11 Aug 2020 21:19:49 -0300 Subject: [PATCH] topotests: new BFD test for multi hop features Add a new test to cover the new features for multi hop BFD peers: - Test that we correctly receive TTL from protocol integration. - Check minimum TTL usage and 'show' command. - Check for passive mode. Signed-off-by: Rafael Zalamena --- tests/topotests/bfd-topo3/__init__.py | 0 tests/topotests/bfd-topo3/r1/bfd-peers.json | 68 +++++++ tests/topotests/bfd-topo3/r1/bfdd.conf | 17 ++ tests/topotests/bfd-topo3/r1/bgpd.conf | 20 ++ tests/topotests/bfd-topo3/r1/zebra.conf | 10 + tests/topotests/bfd-topo3/r2/bfd-peers.json | 46 +++++ tests/topotests/bfd-topo3/r2/bfdd.conf | 15 ++ tests/topotests/bfd-topo3/r2/bgpd.conf | 15 ++ tests/topotests/bfd-topo3/r2/zebra.conf | 14 ++ tests/topotests/bfd-topo3/r3/bfd-peers.json | 68 +++++++ tests/topotests/bfd-topo3/r3/bfdd.conf | 11 ++ tests/topotests/bfd-topo3/r3/bgpd.conf | 19 ++ tests/topotests/bfd-topo3/r3/zebra.conf | 14 ++ tests/topotests/bfd-topo3/r4/bfd-peers.json | 46 +++++ tests/topotests/bfd-topo3/r4/bfdd.conf | 16 ++ tests/topotests/bfd-topo3/r4/bgpd.conf | 16 ++ tests/topotests/bfd-topo3/r4/zebra.conf | 10 + tests/topotests/bfd-topo3/test_bfd_topo3.dot | 73 +++++++ tests/topotests/bfd-topo3/test_bfd_topo3.jpg | Bin 0 -> 34705 bytes tests/topotests/bfd-topo3/test_bfd_topo3.py | 191 +++++++++++++++++++ 20 files changed, 669 insertions(+) create mode 100644 tests/topotests/bfd-topo3/__init__.py create mode 100644 tests/topotests/bfd-topo3/r1/bfd-peers.json create mode 100644 tests/topotests/bfd-topo3/r1/bfdd.conf create mode 100644 tests/topotests/bfd-topo3/r1/bgpd.conf create mode 100644 tests/topotests/bfd-topo3/r1/zebra.conf create mode 100644 tests/topotests/bfd-topo3/r2/bfd-peers.json create mode 100644 tests/topotests/bfd-topo3/r2/bfdd.conf create mode 100644 tests/topotests/bfd-topo3/r2/bgpd.conf create mode 100644 tests/topotests/bfd-topo3/r2/zebra.conf create mode 100644 tests/topotests/bfd-topo3/r3/bfd-peers.json create mode 100644 tests/topotests/bfd-topo3/r3/bfdd.conf create mode 100644 tests/topotests/bfd-topo3/r3/bgpd.conf create mode 100644 tests/topotests/bfd-topo3/r3/zebra.conf create mode 100644 tests/topotests/bfd-topo3/r4/bfd-peers.json create mode 100644 tests/topotests/bfd-topo3/r4/bfdd.conf create mode 100644 tests/topotests/bfd-topo3/r4/bgpd.conf create mode 100644 tests/topotests/bfd-topo3/r4/zebra.conf create mode 100644 tests/topotests/bfd-topo3/test_bfd_topo3.dot create mode 100644 tests/topotests/bfd-topo3/test_bfd_topo3.jpg create mode 100644 tests/topotests/bfd-topo3/test_bfd_topo3.py diff --git a/tests/topotests/bfd-topo3/__init__.py b/tests/topotests/bfd-topo3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/bfd-topo3/r1/bfd-peers.json b/tests/topotests/bfd-topo3/r1/bfd-peers.json new file mode 100644 index 0000000000..56205d538b --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bfd-peers.json @@ -0,0 +1,68 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "2001:db8:1::1", + "minimum-ttl": 253, + "multihop": true, + "passive-mode": true, + "peer": "2001:db8:3::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r1-eth0", + "local": "2001:db8:1::1", + "multihop": false, + "passive-mode": true, + "peer": "2001:db8:1::2", + "receive-interval": 600, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 600, + "remote-transmit-interval": 600, + "status": "up", + "transmit-interval": 600, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "192.168.1.1", + "minimum-ttl": 254, + "multihop": true, + "passive-mode": true, + "peer": "192.168.2.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r1/bfdd.conf b/tests/topotests/bfd-topo3/r1/bfdd.conf new file mode 100644 index 0000000000..8e40b76d41 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bfdd.conf @@ -0,0 +1,17 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile fast-tx + receive-interval 600 + transmit-interval 600 + passive-mode + ! + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + passive-mode + ! +! diff --git a/tests/topotests/bfd-topo3/r1/bgpd.conf b/tests/topotests/bfd-topo3/r1/bgpd.conf new file mode 100644 index 0000000000..a0281d50c6 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/bgpd.conf @@ -0,0 +1,20 @@ +router bgp 100 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::2 remote-as internal + neighbor 2001:db8:1::2 bfd profile fast-tx + neighbor 192.168.2.1 remote-as external + neighbor 192.168.2.1 ebgp-multihop 2 + neighbor 192.168.2.1 bfd profile slow-tx + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 ebgp-multihop 3 + neighbor 2001:db8:3::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::2 activate + neighbor 192.168.2.1 activate + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r1/zebra.conf b/tests/topotests/bfd-topo3/r1/zebra.conf new file mode 100644 index 0000000000..64aee48436 --- /dev/null +++ b/tests/topotests/bfd-topo3/r1/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.1/32 +! +interface r1-eth0 + ip address 192.168.1.1/24 + ipv6 address 2001:db8:1::1/64 +! diff --git a/tests/topotests/bfd-topo3/r2/bfd-peers.json b/tests/topotests/bfd-topo3/r2/bfd-peers.json new file mode 100644 index 0000000000..cb8985b13e --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bfd-peers.json @@ -0,0 +1,46 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r2-eth0", + "local": "2001:db8:1::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:1::1", + "receive-interval": 600, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 600, + "remote-transmit-interval": 600, + "status": "up", + "transmit-interval": 600, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r2-eth1", + "local": "2001:db8:2::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:2::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r2/bfdd.conf b/tests/topotests/bfd-topo3/r2/bfdd.conf new file mode 100644 index 0000000000..2a92e463e0 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bfdd.conf @@ -0,0 +1,15 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile fast-tx + receive-interval 600 + transmit-interval 600 + ! + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! +! diff --git a/tests/topotests/bfd-topo3/r2/bgpd.conf b/tests/topotests/bfd-topo3/r2/bgpd.conf new file mode 100644 index 0000000000..0e96033023 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/bgpd.conf @@ -0,0 +1,15 @@ +router bgp 100 + no bgp ebgp-requires-policy + neighbor 2001:db8:1::1 remote-as internal + neighbor 2001:db8:1::1 bfd profile fast-tx + neighbor 2001:db8:2::1 remote-as external + neighbor 2001:db8:2::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:2::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r2/zebra.conf b/tests/topotests/bfd-topo3/r2/zebra.conf new file mode 100644 index 0000000000..c7e22d4804 --- /dev/null +++ b/tests/topotests/bfd-topo3/r2/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.2/32 +! +interface r2-eth0 + ip address 192.168.1.2/24 + ipv6 address 2001:db8:1::2/64 +! +interface r2-eth1 + ip address 192.168.2.2/24 + ipv6 address 2001:db8:2::2/64 +! diff --git a/tests/topotests/bfd-topo3/r3/bfd-peers.json b/tests/topotests/bfd-topo3/r3/bfd-peers.json new file mode 100644 index 0000000000..8be35fd084 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bfd-peers.json @@ -0,0 +1,68 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r3-eth1", + "local": "2001:db8:3::2", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:3::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r3-eth0", + "local": "2001:db8:2::1", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:2::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "192.168.2.1", + "minimum-ttl": 254, + "multihop": true, + "passive-mode": false, + "peer": "192.168.1.1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r3/bfdd.conf b/tests/topotests/bfd-topo3/r3/bfdd.conf new file mode 100644 index 0000000000..f7972c6ce5 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bfdd.conf @@ -0,0 +1,11 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! +! diff --git a/tests/topotests/bfd-topo3/r3/bgpd.conf b/tests/topotests/bfd-topo3/r3/bgpd.conf new file mode 100644 index 0000000000..e14d2011a0 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/bgpd.conf @@ -0,0 +1,19 @@ +router bgp 300 + no bgp ebgp-requires-policy + neighbor 192.168.1.1 remote-as external + neighbor 192.168.1.1 ebgp-multihop 2 + neighbor 192.168.1.1 bfd profile slow-tx + neighbor 2001:db8:2::2 remote-as external + neighbor 2001:db8:2::2 bfd profile slow-tx + neighbor 2001:db8:3::1 remote-as external + neighbor 2001:db8:3::1 bfd profile slow-tx + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 192.168.1.1 activate + neighbor 2001:db8:2::2 activate + neighbor 2001:db8:3::1 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r3/zebra.conf b/tests/topotests/bfd-topo3/r3/zebra.conf new file mode 100644 index 0000000000..14248fb6f7 --- /dev/null +++ b/tests/topotests/bfd-topo3/r3/zebra.conf @@ -0,0 +1,14 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.3/32 +! +interface r3-eth0 + ip address 192.168.2.1/24 + ipv6 address 2001:db8:2::1/64 +! +interface r3-eth1 + ip address 192.168.3.2/24 + ipv6 address 2001:db8:3::2/64 +! diff --git a/tests/topotests/bfd-topo3/r4/bfd-peers.json b/tests/topotests/bfd-topo3/r4/bfd-peers.json new file mode 100644 index 0000000000..e2e6722ef4 --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bfd-peers.json @@ -0,0 +1,46 @@ +[ + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "local": "2001:db8:3::1", + "minimum-ttl": 253, + "multihop": true, + "passive-mode": false, + "peer": "2001:db8:1::1", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + }, + { + "detect-multiplier": 3, + "diagnostic": "ok", + "echo-interval": 0, + "id": "*", + "interface": "r4-eth0", + "local": "2001:db8:3::1", + "multihop": false, + "passive-mode": false, + "peer": "2001:db8:3::2", + "receive-interval": 2000, + "remote-detect-multiplier": 3, + "remote-diagnostic": "ok", + "remote-echo-interval": 50, + "remote-id": "*", + "remote-receive-interval": 2000, + "remote-transmit-interval": 2000, + "status": "up", + "transmit-interval": 2000, + "uptime": "*", + "vrf": "default" + } +] diff --git a/tests/topotests/bfd-topo3/r4/bfdd.conf b/tests/topotests/bfd-topo3/r4/bfdd.conf new file mode 100644 index 0000000000..f44abc0b8a --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bfdd.conf @@ -0,0 +1,16 @@ +! +debug bfd network +debug bfd peer +debug bfd zebra +! +bfd + profile slow-tx + receive-interval 2000 + transmit-interval 2000 + ! + profile slow-tx-mh + receive-interval 2000 + transmit-interval 2000 + minimum-ttl 250 + ! +! diff --git a/tests/topotests/bfd-topo3/r4/bgpd.conf b/tests/topotests/bfd-topo3/r4/bgpd.conf new file mode 100644 index 0000000000..3e81008d5d --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/bgpd.conf @@ -0,0 +1,16 @@ +router bgp 400 + no bgp ebgp-requires-policy + neighbor 2001:db8:3::2 remote-as external + neighbor 2001:db8:3::2 bfd profile slow-tx + neighbor 2001:db8:1::1 remote-as external + neighbor 2001:db8:1::1 ebgp-multihop 3 + neighbor 2001:db8:1::1 bfd profile slow-tx-mh + address-family ipv4 unicast + redistribute connected + exit-address-family + address-family ipv6 unicast + redistribute connected + neighbor 2001:db8:1::1 activate + neighbor 2001:db8:3::2 activate + exit-address-family +! diff --git a/tests/topotests/bfd-topo3/r4/zebra.conf b/tests/topotests/bfd-topo3/r4/zebra.conf new file mode 100644 index 0000000000..bf0cfcf42c --- /dev/null +++ b/tests/topotests/bfd-topo3/r4/zebra.conf @@ -0,0 +1,10 @@ +ip forwarding +ipv6 forwarding +! +interface lo + ip address 10.254.254.4/32 +! +interface r4-eth0 + ip address 192.168.3.1/24 + ipv6 address 2001:db8:3::1/64 +! diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.dot b/tests/topotests/bfd-topo3/test_bfd_topo3.dot new file mode 100644 index 0000000000..502cea11f2 --- /dev/null +++ b/tests/topotests/bfd-topo3/test_bfd_topo3.dot @@ -0,0 +1,73 @@ +## Color coding: +######################### +## Main FRR: #f08080 red +## Switches: #d0e0d0 gray +## RIP: #19e3d9 Cyan +## RIPng: #fcb314 dark yellow +## OSPFv2: #32b835 Green +## OSPFv3: #19e3d9 Cyan +## ISIS IPv4 #fcb314 dark yellow +## ISIS IPv6 #9a81ec purple +## BGP IPv4 #eee3d3 beige +## BGP IPv6 #fdff00 yellow +##### Colors (see http://www.color-hex.com/) + +graph template { + label="bfd-topo3"; + + # Routers + r1 [ + shape=doubleoctagon, + label="r1", + fillcolor="#f08080", + style=filled, + ]; + r2 [ + shape=doubleoctagon + label="r2", + fillcolor="#f08080", + style=filled, + ]; + r3 [ + shape=doubleoctagon + label="r3", + fillcolor="#f08080", + style=filled, + ]; + r4 [ + shape=doubleoctagon + label="r4", + fillcolor="#f08080", + style=filled, + ]; + + # Switches + sw1 [ + shape=oval, + label="sw1\n192.168.1.0/24\n2001:db8:1::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw2 [ + shape=oval, + label="sw2\n192.168.2.0/24\n2001:db8:2::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + sw3 [ + shape=oval, + label="sw3\n192.168.3.0/24\n2001:db8:3::/64", + fillcolor="#d0e0d0", + style=filled, + ]; + + # Connections + r1 -- sw1 [label="eth0\n.1"]; + r2 -- sw1 [label="eth0\n.2"]; + + r3 -- sw2 [label="eth0\n.1"]; + r2 -- sw2 [label="eth1\n.2"]; + + r4 -- sw3 [label="eth0\n.1"]; + r3 -- sw3 [label="eth2\n.2"]; +} diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.jpg b/tests/topotests/bfd-topo3/test_bfd_topo3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6b532560bf98c04cdfba54b8645f270b7aca1aba GIT binary patch literal 34705 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 BW@P{X literal 0 HcmV?d00001 diff --git a/tests/topotests/bfd-topo3/test_bfd_topo3.py b/tests/topotests/bfd-topo3/test_bfd_topo3.py new file mode 100644 index 0000000000..bcee338a92 --- /dev/null +++ b/tests/topotests/bfd-topo3/test_bfd_topo3.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python + +# +# test_bfd_topo3.py +# Part of NetDEF Topology Tests +# +# Copyright (c) 2020 by +# Network Device Education Foundation, Inc. ("NetDEF") +# +# Permission to use, copy, modify, and/or distribute this software +# for any purpose with or without fee is hereby granted, provided +# that the above copyright notice and this permission notice appear +# in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE +# OF THIS SOFTWARE. +# + +""" +test_bfd_topo3.py: Test the FRR BFD daemon multi hop. +""" + +import os +import sys +import json +from functools import partial +import pytest + +# Save the Current Working Directory to find configuration files. +CWD = os.path.dirname(os.path.realpath(__file__)) +sys.path.append(os.path.join(CWD, "../")) + +# pylint: disable=C0413 +# Import topogen and topotest helpers +from lib import topotest +from lib.topogen import Topogen, TopoRouter, get_topogen +from lib.topolog import logger + +# Required to instantiate the topology builder class. +from mininet.topo import Topo + + +class BFDTopo(Topo): + "Test topology builder" + + def build(self, *_args, **_opts): + "Build function" + tgen = get_topogen(self) + + # Create 4 routers + for routern in range(1, 5): + tgen.add_router("r{}".format(routern)) + + switch = tgen.add_switch("s1") + switch.add_link(tgen.gears["r1"]) + switch.add_link(tgen.gears["r2"]) + + switch = tgen.add_switch("s2") + switch.add_link(tgen.gears["r2"]) + switch.add_link(tgen.gears["r3"]) + + switch = tgen.add_switch("s3") + switch.add_link(tgen.gears["r3"]) + switch.add_link(tgen.gears["r4"]) + + +def setup_module(mod): + "Sets up the pytest environment" + tgen = Topogen(BFDTopo, mod.__name__) + tgen.start_topology() + + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + daemon_file = "{}/{}/bfdd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BFD, daemon_file) + + daemon_file = "{}/{}/zebra.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_ZEBRA, daemon_file) + + daemon_file = "{}/{}/bgpd.conf".format(CWD, rname) + if os.path.isfile(daemon_file): + router.load_config(TopoRouter.RD_BGP, daemon_file) + + # Initialize all routers. + tgen.start_router() + + +def test_wait_bgp_convergence(): + "Wait for BGP to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("waiting for protocols to converge") + + def expect_loopback_route(router, iptype, route, proto): + "Wait until route is present on RIB for protocol." + logger.info('waiting route {} in {}'.format(route, router)) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + 'show {} route json'.format(iptype), + { route: [{ 'protocol': proto }] } + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" OSPF convergence failure'.format(router) + assert result is None, assertmsg + + # Wait for R1 <-> R2 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R1 <-> R3 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.3/32', 'bgp') + # Wait for R1 <-> R4 convergence. + expect_loopback_route('r1', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R2 <-> R1 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R2 <-> R3 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.3/32', 'bgp') + # Wait for R2 <-> R4 convergence. + expect_loopback_route('r2', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R3 <-> R1 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R3 <-> R2 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R3 <-> R4 convergence. + expect_loopback_route('r3', 'ip', '10.254.254.4/32', 'bgp') + + # Wait for R4 <-> R1 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.1/32', 'bgp') + # Wait for R4 <-> R2 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.2/32', 'bgp') + # Wait for R4 <-> R3 convergence. + expect_loopback_route('r4', 'ip', '10.254.254.3/32', 'bgp') + + +def test_wait_bfd_convergence(): + "Wait for BFD to converge" + tgen = get_topogen() + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("test BFD configurations") + + def expect_bfd_configuration(router): + "Load JSON file and compare with 'show bfd peer json'" + logger.info('waiting BFD configuration on router {}'.format(router)) + bfd_config = json.loads(open('{}/{}/bfd-peers.json'.format(CWD, router)).read()) + test_func = partial( + topotest.router_json_cmp, + tgen.gears[router], + 'show bfd peers json', + bfd_config + ) + _, result = topotest.run_and_expect(test_func, None, count=130, wait=1) + assertmsg = '"{}" BFD configuration failure'.format(router) + assert result is None, assertmsg + + expect_bfd_configuration('r1') + expect_bfd_configuration('r2') + expect_bfd_configuration('r3') + expect_bfd_configuration('r4') + + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + tgen.stop_topology() + + +def test_memory_leak(): + "Run the memory leak test and report results." + tgen = get_topogen() + if not tgen.is_memleak_enabled(): + pytest.skip("Memory leak test/report is disabled") + + tgen.report_memory_leaks() + + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) -- 2.39.5