diff --git a/tests/topotests/pbr-topo1/r1/pbr-interface.json b/tests/topotests/pbr-topo1/r1/pbr-interface.json new file mode 100644 index 0000000000..452b24dcd7 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-interface.json @@ -0,0 +1,12 @@ +[ + { + "name":"r1-eth1", + "policy":"EVA", + "valid":true + }, + { + "name":"r1-eth2", + "policy":"DONNA", + "valid":true + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbr-map.json b/tests/topotests/pbr-topo1/r1/pbr-map.json new file mode 100644 index 0000000000..6b9eaa9ceb --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-map.json @@ -0,0 +1,60 @@ +[ + { + "name":"DONNA", + "valid":true, + "policies":[ + { + "id":3, + "sequenceNumber":5, + "ruleNumber":304, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "tableId":10002, + "name":"C", + "installed":true, + "installedInternally":1 + }, + "matchSrc":"1.2.0.0\/16", + "matchDst":"3.4.5.0\/24" + } + ] + }, + { + "name":"EVA", + "valid":true, + "policies":[ + { + "id":1, + "sequenceNumber":5, + "ruleNumber":304, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "tableId":10003, + "name":"EVA5", + "installed":true, + "installedInternally":1 + }, + "matchSrc":"4.5.6.7\/32" + }, + { + "id":2, + "sequenceNumber":10, + "ruleNumber":309, + "vrfUnchanged":false, + "installed":true, + "installedReason":"Valid", + "nexthopGroup":{ + "tableId":10000, + "name":"A", + "installed":true, + "installedInternally":1 + }, + "matchDst":"9.9.9.9\/32" + } + ] + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json new file mode 100644 index 0000000000..ff85438ad5 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbr-nexthop-groups.json @@ -0,0 +1,58 @@ +[ + { + "id":10000, + "name":"A", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.2.2", + "valid":true + }, + { + "nexthop":"192.168.3.2", + "valid":true + }, + { + "nexthop":"192.168.1.2", + "valid":true + } + ] + }, + { + "id":10002, + "name":"C", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.1.44", + "valid":true + } + ] + }, + { + "id":10001, + "name":"B", + "valid":false, + "installed":false, + "nexthops":[ + { + "nexthop":"192.168.50.1", + "valid":false + } + ] + }, + { + "id":10003, + "name":"EVA5", + "valid":true, + "installed":true, + "nexthops":[ + { + "nexthop":"192.168.1.5", + "valid":true + } + ] + } +] diff --git a/tests/topotests/pbr-topo1/r1/pbrd.conf b/tests/topotests/pbr-topo1/r1/pbrd.conf new file mode 100644 index 0000000000..234683f307 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/pbrd.conf @@ -0,0 +1,33 @@ +nexthop-group A + nexthop 192.168.1.2 + nexthop 192.168.2.2 + nexthop 192.168.3.2 + nexhtop 192.168.4.2 +! +# This one is bogus and should +# never work +nexthop-group B + nexthop 192.168.50.1 +! +nexthop-group C + nexthop 192.168.1.44 +! +pbr-map EVA seq 5 + match src-ip 4.5.6.7/32 + set nexthop 192.168.1.5 +! +pbr-map EVA seq 10 + match dst-ip 9.9.9.9/32 + set nexthop-group A +! +pbr-map DONNA seq 5 + match dst-ip 3.4.5.0/24 + match src-ip 1.2.0.0/16 + set nexthop-group C +! + +int r1-eth1 + pbr-policy EVA +! +int r1-eth2 + pbr-policy DONNA diff --git a/tests/topotests/pbr-topo1/r1/zebra.conf b/tests/topotests/pbr-topo1/r1/zebra.conf new file mode 100644 index 0000000000..f29b146a62 --- /dev/null +++ b/tests/topotests/pbr-topo1/r1/zebra.conf @@ -0,0 +1,11 @@ +int r1-eth0 + ip address 192.168.1.1/24 + +int r1-eth1 + ip address 192.168.2.1/24 + +int r1-eth2 + ip address 192.168.3.1/24 + +int r1-eth3 + ip address 192.168.4.1/24 diff --git a/tests/topotests/pbr-topo1/test_pbr_topo1.py b/tests/topotests/pbr-topo1/test_pbr_topo1.py new file mode 100755 index 0000000000..2853165d45 --- /dev/null +++ b/tests/topotests/pbr-topo1/test_pbr_topo1.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python + +# +# test_pbr_topo1.py +# +# Copyright (c) 2020 by +# Cumulus Networks, Inc. +# Donald Sharp +# +# 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_pbr_topo1.py: Testing PBR + +""" + +import os +import re +import sys +import pytest +import json + +# 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 + +##################################################### +## +## Network Topology Definition +## +##################################################### + + +class NetworkTopo(Topo): + "PBR Topology 1" + + def build(self, **_opts): + "Build function" + + tgen = get_topogen(self) + + for routern in range(1, 2): + tgen.add_router("r{}".format(routern)) + + # On main router + # First switch is for a dummy interface (for local network) + switch = tgen.add_switch("sw1") + switch.add_link(tgen.gears["r1"]) + + # Switches for PBR + # switch 2 switch is for connection to PBR router + switch = tgen.add_switch("sw2") + switch.add_link(tgen.gears["r1"]) + + # switch 4 is stub on remote PBR router + switch = tgen.add_switch("sw4") + switch.add_link(tgen.gears["r1"]) + + # switch 3 is between PBR routers + switch = tgen.add_switch("sw3") + switch.add_link(tgen.gears["r1"]) + + +##################################################### +## +## Tests starting +## +##################################################### + + +def setup_module(module): + "Setup topology" + tgen = Topogen(NetworkTopo, module.__name__) + tgen.start_topology() + + # This is a sample of configuration loading. + router_list = tgen.routers() + for rname, router in router_list.iteritems(): + router.load_config( + TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) + ) + router.load_config( + TopoRouter.RD_PBRD, os.path.join(CWD, "{}/pbrd.conf".format(rname)) + ) + + tgen.start_router() + #gen.mininet_cli() + +def teardown_module(_mod): + "Teardown the pytest environment" + tgen = get_topogen() + + # This function tears down the whole topology. + tgen.stop_topology() + + +def test_converge_protocols(): + "Wait for protocol convergence" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + topotest.sleep(5, "Waiting for PBR convergence") + + +def test_pbr_data(): + "Test PBR 'show ip eigrp'" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + # Verify PBR Status + logger.info("Verifying PBR routes") + + router_list = tgen.routers().values() + for router in router_list: + intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name) + + logger.info(intf_file) + # Read expected result from file + expected = json.loads(open(intf_file).read()) + + # Actual output from router + actual = router.vtysh_cmd("show pbr interface json", isjson=True) + + assertmsg = '"show pbr interface" mismatches on {}'.format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + map_file = "{}/{}/pbr-map.json".format(CWD, router.name) + logger.info(map_file) + # Read expected result from file + expected = json.loads(open(map_file).read()) + + # Actual output from router + actual = router.vtysh_cmd("show pbr map json", isjson=True) + + assertmsg = '"show pbr map" mismatches on {}'.format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + + nexthop_file = "{}/{}/pbr-nexthop-groups.json".format(CWD, router.name) + + # Read expected result from file + expected = json.loads(open(nexthop_file).read()) + + # Actual output from router + actual = router.vtysh_cmd("show pbr nexthop-groups json", isjson=True) + + assertmsg = '"show pbr nexthop-groups" mismatches on {}'.format(router.name) + assert topotest.json_cmp(actual, expected) is None, assertmsg + +if __name__ == "__main__": + args = ["-s"] + sys.argv[1:] + sys.exit(pytest.main(args)) +