mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-09 09:16:17 +00:00
Merge pull request #6096 from pjdruddy/vxlan_evpn_topo_test
tests: add basic topotest for VXLAN EVPN
This commit is contained in:
commit
e68a7dbb09
1
tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf
Normal file
1
tests/topotests/bgp-evpn-vxlan_topo1/P1/bgpd.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
!
|
4
tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf
Normal file
4
tests/topotests/bgp-evpn-vxlan_topo1/P1/ospfd.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
!
|
||||||
|
router ospf
|
||||||
|
network 10.20.0.0/16 area 0
|
||||||
|
network 10.20.20.20/32 area 0
|
7
tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf
Normal file
7
tests/topotests/bgp-evpn-vxlan_topo1/P1/zebra.conf
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
!
|
||||||
|
interface lo
|
||||||
|
ip address 10.20.20.20/32
|
||||||
|
interface P1-eth0
|
||||||
|
ip address 10.20.1.2/24
|
||||||
|
interface P1-eth1
|
||||||
|
ip address 10.20.2.2/24
|
10
tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf
Normal file
10
tests/topotests/bgp-evpn-vxlan_topo1/PE1/bgpd.conf
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
router bgp 65000
|
||||||
|
bgp router-id 10.10.10.10
|
||||||
|
no bgp default ipv4-unicast
|
||||||
|
neighbor 10.30.30.30 remote-as 65000
|
||||||
|
neighbor 10.30.30.30 ebgp-multihop 2
|
||||||
|
neighbor 10.30.30.30 update-source lo
|
||||||
|
neighbor 10.30.30.30 capability extended-nexthop
|
||||||
|
address-family l2vpn evpn
|
||||||
|
neighbor 10.30.30.30 activate
|
||||||
|
advertise-all-vni
|
16
tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
Normal file
16
tests/topotests/bgp-evpn-vxlan_topo1/PE1/evpn.vni.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"vni":101,
|
||||||
|
"type":"L2",
|
||||||
|
"vrf":"default",
|
||||||
|
"vxlanInterface":"vxlan101",
|
||||||
|
"ifindex":5,
|
||||||
|
"vtepIp":"10.10.10.10",
|
||||||
|
"mcastGroup":"0.0.0.0",
|
||||||
|
"advertiseGatewayMacip":"No",
|
||||||
|
"numMacs":5,
|
||||||
|
"numArpNd":2,
|
||||||
|
"numRemoteVteps":[
|
||||||
|
"10.30.30.30"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
4
tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf
Normal file
4
tests/topotests/bgp-evpn-vxlan_topo1/PE1/ospfd.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
!
|
||||||
|
router ospf
|
||||||
|
network 10.20.0.0/16 area 0
|
||||||
|
network 10.10.10.10/32 area 0
|
10
tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
Normal file
10
tests/topotests/bgp-evpn-vxlan_topo1/PE1/zebra.conf
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
!
|
||||||
|
log file zebra.log
|
||||||
|
!
|
||||||
|
interface lo
|
||||||
|
ip address 10.10.10.10/32
|
||||||
|
interface PE1-eth0
|
||||||
|
ip address 10.10.1.1/24
|
||||||
|
interface PE1-eth1
|
||||||
|
ip address 10.20.1.1/24
|
||||||
|
!
|
11
tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf
Normal file
11
tests/topotests/bgp-evpn-vxlan_topo1/PE2/bgpd.conf
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
router bgp 65000
|
||||||
|
bgp router-id 10.30.30.30
|
||||||
|
no bgp default ipv4-unicast
|
||||||
|
neighbor 10.10.10.10 remote-as 65000
|
||||||
|
neighbor 10.10.10.10 ebgp-multihop 2
|
||||||
|
neighbor 10.10.10.10 update-source lo
|
||||||
|
neighbor 10.10.10.10 capability extended-nexthop
|
||||||
|
!
|
||||||
|
address-family l2vpn evpn
|
||||||
|
neighbor 10.10.10.10 activate
|
||||||
|
advertise-all-vni
|
15
tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
Normal file
15
tests/topotests/bgp-evpn-vxlan_topo1/PE2/evpn.vni.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"vni":101,
|
||||||
|
"type":"L2",
|
||||||
|
"vrf":"default",
|
||||||
|
"vxlanInterface":"vxlan101",
|
||||||
|
"ifindex":5,
|
||||||
|
"vtepIp":"10.30.30.30",
|
||||||
|
"mcastGroup":"0.0.0.0",
|
||||||
|
"advertiseGatewayMacip":"No",
|
||||||
|
"numMacs":5,
|
||||||
|
"numArpNd":2,
|
||||||
|
"numRemoteVteps":[
|
||||||
|
"10.10.10.10"
|
||||||
|
]
|
||||||
|
}
|
4
tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf
Normal file
4
tests/topotests/bgp-evpn-vxlan_topo1/PE2/ospfd.conf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
!
|
||||||
|
router ospf
|
||||||
|
network 10.20.0.0/16 area 0
|
||||||
|
network 10.30.30.30/32 area 0
|
8
tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
Normal file
8
tests/topotests/bgp-evpn-vxlan_topo1/PE2/zebra.conf
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
!
|
||||||
|
interface lo
|
||||||
|
ip address 10.30.30.30/32
|
||||||
|
interface PE2-eth0
|
||||||
|
ip address 10.20.2.3/24
|
||||||
|
interface PE2-eth1
|
||||||
|
ip address 10.10.1.3/24
|
||||||
|
!
|
0
tests/topotests/bgp-evpn-vxlan_topo1/__init__.py
Normal file
0
tests/topotests/bgp-evpn-vxlan_topo1/__init__.py
Normal file
1
tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf
Normal file
1
tests/topotests/bgp-evpn-vxlan_topo1/host1/bgpd.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
!
|
1
tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf
Normal file
1
tests/topotests/bgp-evpn-vxlan_topo1/host1/ospfd.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
!
|
3
tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf
Normal file
3
tests/topotests/bgp-evpn-vxlan_topo1/host1/zebra.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
!
|
||||||
|
int host1-eth0
|
||||||
|
ip address 10.10.1.55/24
|
1
tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf
Normal file
1
tests/topotests/bgp-evpn-vxlan_topo1/host2/bgpd.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
!
|
1
tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf
Normal file
1
tests/topotests/bgp-evpn-vxlan_topo1/host2/ospfd.conf
Normal file
@ -0,0 +1 @@
|
|||||||
|
!
|
3
tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf
Normal file
3
tests/topotests/bgp-evpn-vxlan_topo1/host2/zebra.conf
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
!
|
||||||
|
interface host2-eth0
|
||||||
|
ip address 10.10.1.56/24
|
289
tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
Executable file
289
tests/topotests/bgp-evpn-vxlan_topo1/test_bgp_evpn_vxlan.py
Executable file
@ -0,0 +1,289 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#
|
||||||
|
# test_bgp_evpn_vxlan.py
|
||||||
|
# Part of NetDEF Topology Tests
|
||||||
|
#
|
||||||
|
# Copyright (c) 2020 by Volta Networks
|
||||||
|
#
|
||||||
|
# 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_bgp_evpn_vxlan.py: Test VXLAN EVPN MAC a route signalling over BGP.
|
||||||
|
"""
|
||||||
|
|
||||||
|
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 TemplateTopo(Topo):
|
||||||
|
"Test topology builder"
|
||||||
|
|
||||||
|
def build(self, *_args, **_opts):
|
||||||
|
"Build function"
|
||||||
|
tgen = get_topogen(self)
|
||||||
|
|
||||||
|
# This function only purpose is to define allocation and relationship
|
||||||
|
# between routers, switches and hosts.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Create routers
|
||||||
|
tgen.add_router("P1")
|
||||||
|
tgen.add_router("PE1")
|
||||||
|
tgen.add_router("PE2")
|
||||||
|
tgen.add_router("host1")
|
||||||
|
tgen.add_router("host2")
|
||||||
|
|
||||||
|
# Host1-PE1
|
||||||
|
switch = tgen.add_switch("s1")
|
||||||
|
switch.add_link(tgen.gears["host1"])
|
||||||
|
switch.add_link(tgen.gears["PE1"])
|
||||||
|
|
||||||
|
# PE1-P1
|
||||||
|
switch = tgen.add_switch("s2")
|
||||||
|
switch.add_link(tgen.gears["PE1"])
|
||||||
|
switch.add_link(tgen.gears["P1"])
|
||||||
|
|
||||||
|
# P1-PE2
|
||||||
|
switch = tgen.add_switch("s3")
|
||||||
|
switch.add_link(tgen.gears["P1"])
|
||||||
|
switch.add_link(tgen.gears["PE2"])
|
||||||
|
|
||||||
|
# PE2-host2
|
||||||
|
switch = tgen.add_switch("s4")
|
||||||
|
switch.add_link(tgen.gears["PE2"])
|
||||||
|
switch.add_link(tgen.gears["host2"])
|
||||||
|
|
||||||
|
|
||||||
|
def setup_module(mod):
|
||||||
|
"Sets up the pytest environment"
|
||||||
|
# This function initiates the topology build with Topogen...
|
||||||
|
tgen = Topogen(TemplateTopo, mod.__name__)
|
||||||
|
# ... and here it calls Mininet initialization functions.
|
||||||
|
tgen.start_topology()
|
||||||
|
|
||||||
|
pe1 = tgen.gears["PE1"]
|
||||||
|
pe2 = tgen.gears["PE2"]
|
||||||
|
p1 = tgen.gears["P1"]
|
||||||
|
|
||||||
|
# set up PE bridges with the EVPN member interfaces facing the CE hosts
|
||||||
|
pe1.run("ip link add name br101 type bridge stp_state 0")
|
||||||
|
pe1.run("ip link set dev br101 up")
|
||||||
|
pe1.run(
|
||||||
|
"ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.10.10.10 nolearning"
|
||||||
|
)
|
||||||
|
pe1.run("ip link set dev vxlan101 master br101")
|
||||||
|
pe1.run("ip link set up dev vxlan101")
|
||||||
|
pe1.run("ip link set dev PE1-eth0 master br101")
|
||||||
|
|
||||||
|
pe2.run("ip link add name br101 type bridge stp_state 0")
|
||||||
|
pe2.run("ip link set dev br101 up")
|
||||||
|
pe2.run(
|
||||||
|
"ip link add vxlan101 type vxlan id 101 dstport 4789 local 10.30.30.30 nolearning"
|
||||||
|
)
|
||||||
|
pe2.run("ip link set dev vxlan101 master br101")
|
||||||
|
pe2.run("ip link set up dev vxlan101")
|
||||||
|
pe2.run("ip link set dev PE2-eth1 master br101")
|
||||||
|
p1.run("sysctl -w net.ipv4.ip_forward=1")
|
||||||
|
|
||||||
|
# This is a sample of configuration loading.
|
||||||
|
router_list = tgen.routers()
|
||||||
|
|
||||||
|
# For all registred routers, load the zebra configuration file
|
||||||
|
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_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
|
||||||
|
)
|
||||||
|
router.load_config(
|
||||||
|
TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
|
||||||
|
)
|
||||||
|
|
||||||
|
# After loading the configurations, this function loads configured daemons.
|
||||||
|
tgen.start_router()
|
||||||
|
|
||||||
|
|
||||||
|
def teardown_module(mod):
|
||||||
|
"Teardown the pytest environment"
|
||||||
|
tgen = get_topogen()
|
||||||
|
|
||||||
|
# This function tears down the whole topology.
|
||||||
|
tgen.stop_topology()
|
||||||
|
|
||||||
|
|
||||||
|
def test_pe1_converge_evpn():
|
||||||
|
"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)
|
||||||
|
|
||||||
|
pe1 = tgen.gears["PE1"]
|
||||||
|
json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name)
|
||||||
|
expected = json.loads(open(json_file).read())
|
||||||
|
|
||||||
|
test_func = partial(
|
||||||
|
topotest.router_json_cmp, pe1, "show evpn vni 101 json", expected
|
||||||
|
)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
|
||||||
|
assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
|
||||||
|
assert result is None, assertmsg
|
||||||
|
# tgen.mininet_cli()
|
||||||
|
|
||||||
|
|
||||||
|
def test_pe2_converge_evpn():
|
||||||
|
"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)
|
||||||
|
|
||||||
|
pe2 = tgen.gears["PE2"]
|
||||||
|
json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name)
|
||||||
|
expected = json.loads(open(json_file).read())
|
||||||
|
|
||||||
|
test_func = partial(
|
||||||
|
topotest.router_json_cmp, pe2, "show evpn vni 101 json", expected
|
||||||
|
)
|
||||||
|
_, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
|
||||||
|
assertmsg = '"{}" JSON output mismatches'.format(pe2.name)
|
||||||
|
assert result is None, assertmsg
|
||||||
|
# tgen.mininet_cli()
|
||||||
|
|
||||||
|
|
||||||
|
def mac_learn_test(host, local):
|
||||||
|
"check the host MAC gets learned by the VNI"
|
||||||
|
|
||||||
|
host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name))
|
||||||
|
int_lines = host_output.splitlines()
|
||||||
|
line_items = int_lines[7].split(": ")
|
||||||
|
mac = line_items[1]
|
||||||
|
mac_output = local.vtysh_cmd("show evpn mac vni 101 mac {} json".format(mac))
|
||||||
|
mac_output_json = json.loads(mac_output)
|
||||||
|
assertmsg = "Local MAC output does not match interface mac {}".format(mac)
|
||||||
|
assert mac_output_json[mac]["type"] == "local"
|
||||||
|
|
||||||
|
|
||||||
|
def mac_test_local_remote(local, remote):
|
||||||
|
"test MAC transfer between local and remote"
|
||||||
|
|
||||||
|
local_output = local.vtysh_cmd("show evpn mac vni all json")
|
||||||
|
remote_output = remote.vtysh_cmd("show evpn mac vni all json")
|
||||||
|
local_output_vni = local.vtysh_cmd("show evpn vni detail json")
|
||||||
|
local_output_json = json.loads(local_output)
|
||||||
|
remote_output_json = json.loads(remote_output)
|
||||||
|
local_output_vni_json = json.loads(local_output_vni)
|
||||||
|
|
||||||
|
for vni in local_output_json:
|
||||||
|
mac_list = local_output_json[vni]["macs"]
|
||||||
|
for mac in mac_list:
|
||||||
|
if mac_list[mac]["type"] == "local" and mac_list[mac]["intf"] != "br101":
|
||||||
|
assertmsg = "JSON output mismatches local: {} remote: {}".format(
|
||||||
|
local_output_vni_json[0]["vtepIp"],
|
||||||
|
remote_output_json[vni]["macs"][mac]["remoteVtep"],
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
remote_output_json[vni]["macs"][mac]["remoteVtep"]
|
||||||
|
== local_output_vni_json[0]["vtepIp"]
|
||||||
|
), assertmsg
|
||||||
|
|
||||||
|
|
||||||
|
def test_learning_pe1():
|
||||||
|
"test MAC learning on PE1"
|
||||||
|
|
||||||
|
tgen = get_topogen()
|
||||||
|
# Don't run this test if we have any failure.
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
host1 = tgen.gears["host1"]
|
||||||
|
pe1 = tgen.gears["PE1"]
|
||||||
|
mac_learn_test(host1, pe1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_learning_pe2():
|
||||||
|
"test MAC learning on PE2"
|
||||||
|
|
||||||
|
tgen = get_topogen()
|
||||||
|
# Don't run this test if we have any failure.
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
host2 = tgen.gears["host2"]
|
||||||
|
pe2 = tgen.gears["PE2"]
|
||||||
|
mac_learn_test(host2, pe2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_remote_mac_pe1():
|
||||||
|
" Test MAC transfer PE1 local and PE2 remote"
|
||||||
|
|
||||||
|
tgen = get_topogen()
|
||||||
|
# Don't run this test if we have any failure.
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
pe1 = tgen.gears["PE1"]
|
||||||
|
pe2 = tgen.gears["PE2"]
|
||||||
|
mac_test_local_remote(pe1, pe2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_local_remote_mac_pe2():
|
||||||
|
" Test MAC transfer PE2 local and PE1 remote"
|
||||||
|
|
||||||
|
tgen = get_topogen()
|
||||||
|
# Don't run this test if we have any failure.
|
||||||
|
if tgen.routers_have_failure():
|
||||||
|
pytest.skip(tgen.errors)
|
||||||
|
|
||||||
|
pe1 = tgen.gears["PE1"]
|
||||||
|
pe2 = tgen.gears["PE2"]
|
||||||
|
mac_test_local_remote(pe2, pe1)
|
||||||
|
|
||||||
|
# Memory leak test template
|
||||||
|
|
||||||
|
|
||||||
|
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))
|
Loading…
Reference in New Issue
Block a user