mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-17 20:09:38 +00:00
Move common function and class to topotest library
Signed-off-by: Martin Winter <mwinter@opensourcerouting.org>
This commit is contained in:
parent
f03e38f397
commit
594b1259f9
1
tests/topotests/.gitignore
vendored
1
tests/topotests/.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
.cache
|
.cache
|
||||||
__pycache__
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
"""
|
"""
|
||||||
test_bgp_multiview_topo1.py: Simple Quagga Route-Server Test
|
test_bgp_multiview_topo1.py: Simple Quagga/FRR Route-Server Test
|
||||||
|
|
||||||
+----------+ +----------+ +----------+ +----------+ +----------+
|
+----------+ +----------+ +----------+ +----------+ +----------+
|
||||||
| peer1 | | peer2 | | peer3 | | peer4 | | peer5 |
|
| peer1 | | peer2 | | peer3 | | peer4 | | peer5 |
|
||||||
@ -47,7 +47,7 @@ test_bgp_multiview_topo1.py: Simple Quagga Route-Server Test
|
|||||||
|
|
|
|
||||||
| .254
|
| .254
|
||||||
+---------+---------+
|
+---------+---------+
|
||||||
| Quagga R1 |
|
| FRR R1 |
|
||||||
| BGP Multi-View |
|
| BGP Multi-View |
|
||||||
| Peer 1-3 > View 1 |
|
| Peer 1-3 > View 1 |
|
||||||
| Peer 4-5 > View 2 |
|
| Peer 4-5 > View 2 |
|
||||||
@ -66,9 +66,8 @@ import os
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import difflib
|
import difflib
|
||||||
import StringIO
|
import pytest
|
||||||
import glob
|
from time import sleep
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from mininet.topo import Topo
|
from mininet.topo import Topo
|
||||||
from mininet.net import Mininet
|
from mininet.net import Mininet
|
||||||
@ -78,151 +77,12 @@ from mininet.cli import CLI
|
|||||||
from mininet.link import Intf
|
from mininet.link import Intf
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
import pytest
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from lib import topotest
|
||||||
|
|
||||||
fatal_error = ""
|
fatal_error = ""
|
||||||
|
|
||||||
def int2dpid(dpid):
|
|
||||||
"Converting Integer to DPID"
|
|
||||||
|
|
||||||
try:
|
|
||||||
dpid = hex(dpid)[2:]
|
|
||||||
dpid = '0'*(16-len(dpid))+dpid
|
|
||||||
return dpid
|
|
||||||
except IndexError:
|
|
||||||
raise Exception('Unable to derive default datapath ID - '
|
|
||||||
'please either specify a dpid or use a '
|
|
||||||
'canonical switch name such as s23.')
|
|
||||||
|
|
||||||
class LinuxRouter(Node):
|
|
||||||
"A Node with IPv4/IPv6 forwarding enabled."
|
|
||||||
|
|
||||||
def config(self, **params):
|
|
||||||
super(LinuxRouter, self).config(**params)
|
|
||||||
# Enable forwarding on the router
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=1')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
|
|
||||||
def terminate(self):
|
|
||||||
"""
|
|
||||||
Terminate generic LinuxRouter Mininet instance
|
|
||||||
"""
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=0')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=0')
|
|
||||||
super(LinuxRouter, self).terminate()
|
|
||||||
|
|
||||||
class QuaggaRouter(Node):
|
|
||||||
"A Node with IPv4/IPv6 forwarding enabled and Quagga as Routing Engine"
|
|
||||||
|
|
||||||
def config(self, **params):
|
|
||||||
super(QuaggaRouter, self).config(**params)
|
|
||||||
# Check if Quagga or FRR is installed
|
|
||||||
if os.path.isfile('/usr/lib/frr/zebra'):
|
|
||||||
self.routertype = 'frr'
|
|
||||||
elif os.path.isfile('/usr/lib/quagga/zebra'):
|
|
||||||
self.routertype = 'quagga'
|
|
||||||
else:
|
|
||||||
raise Exception('No FRR or Quagga found in ususal location')
|
|
||||||
# Enable forwarding on the router
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=1')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
|
|
||||||
# Enable coredumps
|
|
||||||
self.cmd('sysctl kernel.core_uses_pid=1')
|
|
||||||
self.cmd('sysctl fs.suid_dumpable=2')
|
|
||||||
self.cmd("sysctl kernel.core_pattern=/tmp/%s_%%e_core-sig_%%s-pid_%%p.dmp" % self.name)
|
|
||||||
self.cmd('ulimit -c unlimited')
|
|
||||||
# Set ownership of config files
|
|
||||||
self.cmd('chown %s:%svty /etc/%s' % (self.routertype, self.routertype, self.routertype))
|
|
||||||
self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0,
|
|
||||||
'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0}
|
|
||||||
def terminate(self):
|
|
||||||
# Delete Running Quagga Daemons
|
|
||||||
rundaemons = self.cmd('ls -1 /var/run/quagga/*.pid')
|
|
||||||
for d in StringIO.StringIO(rundaemons):
|
|
||||||
self.cmd('kill -7 `cat %s`' % d.rstrip())
|
|
||||||
self.waitOutput()
|
|
||||||
# Disable forwarding
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=0')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=0')
|
|
||||||
super(QuaggaRouter, self).terminate()
|
|
||||||
def removeIPs(self):
|
|
||||||
for interface in self.intfNames():
|
|
||||||
self.cmd('ip address flush', interface)
|
|
||||||
def loadConf(self, daemon, source=None):
|
|
||||||
# print "Daemons before:", self.daemons
|
|
||||||
if daemon in self.daemons.keys():
|
|
||||||
self.daemons[daemon] = 1
|
|
||||||
if source is None:
|
|
||||||
self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
else:
|
|
||||||
self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
else:
|
|
||||||
print("No daemon %s known" % daemon)
|
|
||||||
# print "Daemons after:", self.daemons
|
|
||||||
def startQuagga(self):
|
|
||||||
# Disable integrated-vtysh-config
|
|
||||||
### self.cmd('echo "no service integrated-vtysh-config" > /etc/%s/vtysh.conf' % self.routertype)
|
|
||||||
with open('/etc/%s/vtysh.conf' % self.routertype, "w") as vtyshfile:
|
|
||||||
vtyshfile.write('no service integrated-vtysh-config')
|
|
||||||
self.cmd('chown %s:%svty /etc/%s/vtysh.conf' % (self.routertype, self.routertype, self.routertype))
|
|
||||||
# Try to find relevant old logfiles in /tmp and delete them
|
|
||||||
map(os.remove, glob.glob("/tmp/*%s*.log" % self.name))
|
|
||||||
# Remove old core files
|
|
||||||
map(os.remove, glob.glob("/tmp/%s*.dmp" % self.name))
|
|
||||||
# Remove IP addresses from OS first - we have them in zebra.conf
|
|
||||||
self.removeIPs()
|
|
||||||
# Start Zebra first
|
|
||||||
if self.daemons['zebra'] == 1:
|
|
||||||
self.cmd('/usr/lib/%s/zebra -d' % self.routertype)
|
|
||||||
self.waitOutput()
|
|
||||||
print('%s: %s zebra started' % (self, self.routertype))
|
|
||||||
sleep(1)
|
|
||||||
# Fix Link-Local Addresses
|
|
||||||
# Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
|
|
||||||
self.cmd('for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=\':\'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done')
|
|
||||||
# Now start all the other daemons
|
|
||||||
for daemon in self.daemons:
|
|
||||||
if (self.daemons[daemon] == 1) and (daemon != 'zebra'):
|
|
||||||
self.cmd('/usr/lib/%s/%s -d' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
print('%s: %s %s started' % (self, self.routertype, daemon))
|
|
||||||
def checkQuaggaRunning(self):
|
|
||||||
global fatal_error
|
|
||||||
|
|
||||||
daemonsRunning = self.cmd('vtysh -c "show log" | grep "Logging configuration for"')
|
|
||||||
failed = []
|
|
||||||
for daemon in self.daemons:
|
|
||||||
if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning):
|
|
||||||
sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon))
|
|
||||||
# Look for core file
|
|
||||||
corefiles = glob.glob("/tmp/%s_%s_core*.dmp" % (self.name, daemon))
|
|
||||||
if (len(corefiles) > 0):
|
|
||||||
backtrace = subprocess.check_output(["gdb /usr/lib/%s/%s %s --batch -ex bt 2> /dev/null" % (self.routertype, daemon, corefiles[0])], shell=True)
|
|
||||||
sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon))
|
|
||||||
sys.stderr.write("%s\n" % backtrace)
|
|
||||||
else:
|
|
||||||
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
|
|
||||||
if os.path.isfile("/tmp/%s-%s.log" % (self.name, daemon)):
|
|
||||||
log_tail = subprocess.check_output(["tail -n20 /tmp/%s-%s.log 2> /dev/null" % (self.name, daemon)], shell=True)
|
|
||||||
sys.stderr.write("\nFrom %s %s %s log file:\n" % (self.routertype, self.name, daemon))
|
|
||||||
sys.stderr.write("%s\n" % log_tail)
|
|
||||||
failed += [daemon]
|
|
||||||
return failed
|
|
||||||
|
|
||||||
class LegacySwitch(OVSSwitch):
|
|
||||||
"A Legacy Switch without OpenFlow"
|
|
||||||
|
|
||||||
def __init__(self, name, **params):
|
|
||||||
OVSSwitch.__init__(self, name, failMode='standalone', **params)
|
|
||||||
self.switchIP = None
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################
|
#####################################################
|
||||||
##
|
##
|
||||||
@ -231,24 +91,18 @@ class LegacySwitch(OVSSwitch):
|
|||||||
#####################################################
|
#####################################################
|
||||||
|
|
||||||
class NetworkTopo(Topo):
|
class NetworkTopo(Topo):
|
||||||
"A LinuxRouter connecting three IP subnets"
|
"BGP Multiview Topology 1"
|
||||||
|
|
||||||
def build(self, **_opts):
|
def build(self, **_opts):
|
||||||
|
|
||||||
quaggaPrivateDirs = ['/etc/quagga',
|
|
||||||
'/etc/frr',
|
|
||||||
'/var/run/quagga',
|
|
||||||
'/var/run/frr',
|
|
||||||
'/var/log']
|
|
||||||
exabgpPrivateDirs = ['/etc/exabgp',
|
exabgpPrivateDirs = ['/etc/exabgp',
|
||||||
'/var/run/exabgp',
|
'/var/run/exabgp',
|
||||||
'/var/log']
|
'/var/log']
|
||||||
|
|
||||||
# Setup Routers
|
# Setup Routers
|
||||||
quagga = {}
|
router = {}
|
||||||
for i in range(1, 2):
|
for i in range(1, 2):
|
||||||
quagga[i] = self.addNode('r%s' % i, cls=QuaggaRouter,
|
router[i] = topotest.addRouter(self, 'r%s' % i)
|
||||||
privateDirs=quaggaPrivateDirs)
|
|
||||||
|
|
||||||
# Setup Provider BGP peers
|
# Setup Provider BGP peers
|
||||||
peer = {}
|
peer = {}
|
||||||
@ -260,11 +114,11 @@ class NetworkTopo(Topo):
|
|||||||
# Setup Switches
|
# Setup Switches
|
||||||
switch = {}
|
switch = {}
|
||||||
# First switch is for a dummy interface (for local network)
|
# First switch is for a dummy interface (for local network)
|
||||||
switch[0] = self.addSwitch('sw0', cls=LegacySwitch)
|
switch[0] = self.addSwitch('sw0', cls=topotest.LegacySwitch)
|
||||||
self.addLink(switch[0], quagga[1], intfName2='r1-stub')
|
self.addLink(switch[0], router[1], intfName2='r1-stub')
|
||||||
# Second switch is for connection to all peering routers
|
# Second switch is for connection to all peering routers
|
||||||
switch[1] = self.addSwitch('sw1', cls=LegacySwitch)
|
switch[1] = self.addSwitch('sw1', cls=topotest.LegacySwitch)
|
||||||
self.addLink(switch[1], quagga[1], intfName2='r1-eth0')
|
self.addLink(switch[1], router[1], intfName2='r1-eth0')
|
||||||
for j in range(1, 9):
|
for j in range(1, 9):
|
||||||
self.addLink(switch[1], peer[j], intfName2='peer%s-eth0' % j)
|
self.addLink(switch[1], peer[j], intfName2='peer%s-eth0' % j)
|
||||||
|
|
||||||
@ -294,7 +148,7 @@ def setup_module(module):
|
|||||||
for i in range(1, 2):
|
for i in range(1, 2):
|
||||||
net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i))
|
net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i))
|
||||||
net['r%s' % i].loadConf('bgpd', '%s/r%s/bgpd.conf' % (thisDir, i))
|
net['r%s' % i].loadConf('bgpd', '%s/r%s/bgpd.conf' % (thisDir, i))
|
||||||
net['r%s' % i].startQuagga()
|
net['r%s' % i].startRouter()
|
||||||
|
|
||||||
# Starting PE Hosts and init ExaBGP on each of them
|
# Starting PE Hosts and init ExaBGP on each of them
|
||||||
print('*** Starting BGP on all 8 Peers in 10s')
|
print('*** Starting BGP on all 8 Peers in 10s')
|
||||||
@ -309,7 +163,7 @@ def setup_module(module):
|
|||||||
print('peer%s' % i),
|
print('peer%s' % i),
|
||||||
print('')
|
print('')
|
||||||
|
|
||||||
# For debugging after starting Quagga daemons, uncomment the next line
|
# For debugging after starting Quagga/FRR daemons, uncomment the next line
|
||||||
# CLI(net)
|
# CLI(net)
|
||||||
|
|
||||||
def teardown_module(module):
|
def teardown_module(module):
|
||||||
@ -327,7 +181,7 @@ def teardown_module(module):
|
|||||||
# End - Shutdown network
|
# End - Shutdown network
|
||||||
net.stop()
|
net.stop()
|
||||||
|
|
||||||
def test_quagga_running():
|
def test_router_running():
|
||||||
global fatal_error
|
global fatal_error
|
||||||
global net
|
global net
|
||||||
|
|
||||||
@ -339,15 +193,14 @@ def test_quagga_running():
|
|||||||
print("******************************************\n")
|
print("******************************************\n")
|
||||||
sleep(5)
|
sleep(5)
|
||||||
|
|
||||||
# CLI(net)
|
# Starting Routers
|
||||||
failedRunning = ""
|
|
||||||
for i in range(1, 2):
|
for i in range(1, 2):
|
||||||
failedDaemon = net['r%s' % i].checkQuaggaRunning()
|
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||||
if failedDaemon:
|
assert fatal_error == "", fatal_error
|
||||||
failedRunning += " Daemons failed on r%s: %s\n" % (i, failedDaemon)
|
|
||||||
if failedRunning:
|
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||||
fatal_error = "Some Daemons failed to start or crashed"
|
# CLI(net)
|
||||||
assert False, "Daemons failed to start or crashed:\n%s" % failedRunning
|
|
||||||
|
|
||||||
def test_bgp_converge():
|
def test_bgp_converge():
|
||||||
"Check for BGP converged on all peers and BGP views"
|
"Check for BGP converged on all peers and BGP views"
|
||||||
@ -398,7 +251,7 @@ def test_bgp_converge():
|
|||||||
# print("\nwaiting 15s for routes to populate")
|
# print("\nwaiting 15s for routes to populate")
|
||||||
# sleep(15)
|
# sleep(15)
|
||||||
|
|
||||||
# For debugging after starting Quagga daemons, uncomment the next line
|
# For debugging after starting Quagga/FRR daemons, uncomment the next line
|
||||||
# CLI(net)
|
# CLI(net)
|
||||||
|
|
||||||
def test_bgp_routingTable():
|
def test_bgp_routingTable():
|
||||||
@ -453,7 +306,7 @@ def test_bgp_routingTable():
|
|||||||
|
|
||||||
assert failures == 0, "Routing Table verification failed for router r%s, view %s:\n%s" % (i, view, diff)
|
assert failures == 0, "Routing Table verification failed for router r%s, view %s:\n%s" % (i, view, diff)
|
||||||
|
|
||||||
# For debugging after starting Quagga daemons, uncomment the next line
|
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||||
# CLI(net)
|
# CLI(net)
|
||||||
|
|
||||||
|
|
||||||
|
@ -63,10 +63,8 @@ import os
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import difflib
|
import difflib
|
||||||
import StringIO
|
import pytest
|
||||||
import glob
|
from time import sleep
|
||||||
import subprocess
|
|
||||||
import platform
|
|
||||||
|
|
||||||
from mininet.topo import Topo
|
from mininet.topo import Topo
|
||||||
from mininet.net import Mininet
|
from mininet.net import Mininet
|
||||||
@ -75,173 +73,11 @@ from mininet.log import setLogLevel, info
|
|||||||
from mininet.cli import CLI
|
from mininet.cli import CLI
|
||||||
from mininet.link import Intf
|
from mininet.link import Intf
|
||||||
|
|
||||||
from functools import partial
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
from time import sleep
|
from lib import topotest
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
fatal_error = ""
|
fatal_error = ""
|
||||||
|
|
||||||
def int2dpid(dpid):
|
|
||||||
"Converting Integer to DPID"
|
|
||||||
|
|
||||||
try:
|
|
||||||
dpid = hex(dpid)[2:]
|
|
||||||
dpid = '0'*(16-len(dpid))+dpid
|
|
||||||
return dpid
|
|
||||||
except IndexError:
|
|
||||||
raise Exception('Unable to derive default datapath ID - '
|
|
||||||
'please either specify a dpid or use a '
|
|
||||||
'canonical switch name such as s23.')
|
|
||||||
|
|
||||||
class LinuxRouter(Node):
|
|
||||||
"A Node with IPv4/IPv6 forwarding enabled."
|
|
||||||
|
|
||||||
def config(self, **params):
|
|
||||||
super(LinuxRouter, self).config(**params)
|
|
||||||
# Enable forwarding on the router
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=1')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
|
|
||||||
def terminate(self):
|
|
||||||
"""
|
|
||||||
Terminate generic LinuxRouter Mininet instance
|
|
||||||
"""
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=0')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=0')
|
|
||||||
super(LinuxRouter, self).terminate()
|
|
||||||
|
|
||||||
class QuaggaRouter(Node):
|
|
||||||
"A Node with IPv4/IPv6 forwarding enabled and Quagga as Routing Engine"
|
|
||||||
|
|
||||||
def config(self, **params):
|
|
||||||
super(QuaggaRouter, self).config(**params)
|
|
||||||
# Check if Quagga or FRR is installed
|
|
||||||
if os.path.isfile('/usr/lib/frr/zebra'):
|
|
||||||
self.routertype = 'frr'
|
|
||||||
elif os.path.isfile('/usr/lib/quagga/zebra'):
|
|
||||||
self.routertype = 'quagga'
|
|
||||||
else:
|
|
||||||
raise Exception('No FRR or Quagga found in ususal location')
|
|
||||||
# Enable forwarding on the router
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=1')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
|
|
||||||
# Enable coredumps
|
|
||||||
self.cmd('sysctl kernel.core_uses_pid=1')
|
|
||||||
self.cmd('sysctl fs.suid_dumpable=2')
|
|
||||||
self.cmd("sysctl kernel.core_pattern=/tmp/%s_%%e_core-sig_%%s-pid_%%p.dmp" % self.name)
|
|
||||||
self.cmd('ulimit -c unlimited')
|
|
||||||
# Set ownership of config files
|
|
||||||
self.cmd('chown %s:%svty /etc/%s' % (self.routertype, self.routertype, self.routertype))
|
|
||||||
self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0,
|
|
||||||
'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0,
|
|
||||||
'ldpd': 0}
|
|
||||||
def terminate(self):
|
|
||||||
# Delete Running Quagga Daemons
|
|
||||||
rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype)
|
|
||||||
for d in StringIO.StringIO(rundaemons):
|
|
||||||
self.cmd('kill -7 `cat %s`' % d.rstrip())
|
|
||||||
self.waitOutput()
|
|
||||||
# Disable forwarding
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=0')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=0')
|
|
||||||
super(QuaggaRouter, self).terminate()
|
|
||||||
def removeIPs(self):
|
|
||||||
for interface in self.intfNames():
|
|
||||||
self.cmd('ip address flush', interface)
|
|
||||||
def loadConf(self, daemon, source=None):
|
|
||||||
# print "Daemons before:", self.daemons
|
|
||||||
if daemon in self.daemons.keys():
|
|
||||||
self.daemons[daemon] = 1
|
|
||||||
if source is None:
|
|
||||||
self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
else:
|
|
||||||
self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
else:
|
|
||||||
print("No daemon %s known" % daemon)
|
|
||||||
# print "Daemons after:", self.daemons
|
|
||||||
def startQuagga(self):
|
|
||||||
global fatal_error
|
|
||||||
|
|
||||||
# Disable integrated-vtysh-config
|
|
||||||
with open('/etc/%s/vtysh.conf' % self.routertype, "w") as vtyshfile:
|
|
||||||
vtyshfile.write('no service integrated-vtysh-config')
|
|
||||||
self.cmd('chown %s:%svty /etc/%s/vtysh.conf' % (self.routertype, self.routertype, self.routertype))
|
|
||||||
# Try to find relevant old logfiles in /tmp and delete them
|
|
||||||
map(os.remove, glob.glob("/tmp/*%s*.log" % self.name))
|
|
||||||
# Remove old core files
|
|
||||||
map(os.remove, glob.glob("/tmp/%s*.dmp" % self.name))
|
|
||||||
# Remove IP addresses from OS first - we have them in zebra.conf
|
|
||||||
self.removeIPs()
|
|
||||||
# If ldp is used, check for LDP to be compiled and Linux Kernel to be 4.5 or higher
|
|
||||||
# No error - but return message and skip all the tests
|
|
||||||
if self.daemons['ldpd'] == 1:
|
|
||||||
if not os.path.isfile('/usr/lib/%s/ldpd' % self.routertype):
|
|
||||||
fatal_error = "LDP Test, but no ldpd compiled or installed"
|
|
||||||
print("LDP Test, but no ldpd compiled or installed")
|
|
||||||
return
|
|
||||||
kernel_version = re.search(r'([0-9]+\.[0-9]+).*', platform.release())
|
|
||||||
if kernel_version:
|
|
||||||
if float(kernel_version.group(1)) < 4.5:
|
|
||||||
fatal_error = "LDP Test need Linux Kernel 4.5 minimum"
|
|
||||||
print("LDP Test need Linux Kernel 4.5 minimum")
|
|
||||||
return
|
|
||||||
# Add mpls modules to kernel if we use LDP
|
|
||||||
if self.daemons['ldpd'] == 1:
|
|
||||||
self.cmd('/sbin/modprobe mpls-router')
|
|
||||||
self.cmd('/sbin/modprobe mpls-iptunnel')
|
|
||||||
self.cmd('echo 100000 > /proc/sys/net/mpls/platform_labels')
|
|
||||||
# Start Zebra first
|
|
||||||
if self.daemons['zebra'] == 1:
|
|
||||||
self.cmd('/usr/lib/%s/zebra -d' % self.routertype)
|
|
||||||
self.waitOutput()
|
|
||||||
print('%s: %s zebra started' % (self, self.routertype))
|
|
||||||
sleep(1)
|
|
||||||
# Fix Link-Local Addresses
|
|
||||||
# Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
|
|
||||||
self.cmd('for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=\':\'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done')
|
|
||||||
# Now start all the other daemons
|
|
||||||
for daemon in self.daemons:
|
|
||||||
if (self.daemons[daemon] == 1) and (daemon != 'zebra'):
|
|
||||||
self.cmd('/usr/lib/%s/%s -d' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
print('%s: %s %s started' % (self, self.routertype, daemon))
|
|
||||||
def checkQuaggaRunning(self):
|
|
||||||
global fatal_error
|
|
||||||
|
|
||||||
daemonsRunning = self.cmd('vtysh -c "show log" | grep "Logging configuration for"')
|
|
||||||
for daemon in self.daemons:
|
|
||||||
if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning):
|
|
||||||
sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon))
|
|
||||||
# Look for core file
|
|
||||||
corefiles = glob.glob("/tmp/%s_%s_core*.dmp" % (self.name, daemon))
|
|
||||||
if (len(corefiles) > 0):
|
|
||||||
backtrace = subprocess.check_output(["gdb /usr/lib/%s/%s %s --batch -ex bt 2> /dev/null" % (self.routertype, daemon, corefiles[0])], shell=True)
|
|
||||||
sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon))
|
|
||||||
sys.stderr.write("%s\n" % backtrace)
|
|
||||||
else:
|
|
||||||
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
|
|
||||||
if os.path.isfile("/tmp/%s-%s.log" % (self.name, daemon)):
|
|
||||||
log_tail = subprocess.check_output(["tail -n20 /tmp/%s-%s.log 2> /dev/null" % (self.name, daemon)], shell=True)
|
|
||||||
sys.stderr.write("\nFrom %s %s %s log file:\n" % (self.routertype, self.name, daemon))
|
|
||||||
sys.stderr.write("%s\n" % log_tail)
|
|
||||||
|
|
||||||
fatal_error = "%s: Daemon %s not running" % (self.name, daemon)
|
|
||||||
assert False, "%s: Daemon %s not running" % (self.name, daemon)
|
|
||||||
|
|
||||||
class LegacySwitch(OVSSwitch):
|
|
||||||
"A Legacy Switch without OpenFlow"
|
|
||||||
|
|
||||||
def __init__(self, name, **params):
|
|
||||||
OVSSwitch.__init__(self, name, failMode='standalone', **params)
|
|
||||||
self.switchIP = None
|
|
||||||
|
|
||||||
|
|
||||||
#####################################################
|
#####################################################
|
||||||
##
|
##
|
||||||
## Network Topology Definition
|
## Network Topology Definition
|
||||||
@ -249,41 +85,32 @@ class LegacySwitch(OVSSwitch):
|
|||||||
#####################################################
|
#####################################################
|
||||||
|
|
||||||
class NetworkTopo(Topo):
|
class NetworkTopo(Topo):
|
||||||
"A LinuxRouter connecting three IP subnets"
|
"LDP Test Topology 1"
|
||||||
|
|
||||||
def build(self, **_opts):
|
def build(self, **_opts):
|
||||||
|
|
||||||
quaggaPrivateDirs = ['/etc/quagga',
|
|
||||||
'/etc/frr',
|
|
||||||
'/var/run/quagga',
|
|
||||||
'/var/run/frr',
|
|
||||||
'/var/log']
|
|
||||||
exabgpPrivateDirs = ['/etc/exabgp',
|
|
||||||
'/var/run/exabgp',
|
|
||||||
'/var/log']
|
|
||||||
|
|
||||||
# Setup Routers
|
# Setup Routers
|
||||||
router = {}
|
router = {}
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
router[i] = self.addNode('r%s' % i, cls=QuaggaRouter,
|
router[i] = topotest.addRouter(self, 'r%s' % i)
|
||||||
privateDirs=quaggaPrivateDirs)
|
|
||||||
|
|
||||||
# Setup Switches
|
# Setup Switches, add Interfaces and Connections
|
||||||
switch = {}
|
switch = {}
|
||||||
# First switch
|
# First switch
|
||||||
switch[0] = self.addSwitch('sw0', cls=LegacySwitch)
|
switch[0] = self.addSwitch('sw0', cls=topotest.LegacySwitch)
|
||||||
self.addLink(switch[0], router[1], intfName2='r1-eth0', addr1='80:AA:00:00:00:00', addr2='00:11:00:01:00:00')
|
self.addLink(switch[0], router[1], intfName2='r1-eth0', addr1='80:AA:00:00:00:00', addr2='00:11:00:01:00:00')
|
||||||
self.addLink(switch[0], router[2], intfName2='r2-eth0', addr1='80:AA:00:00:00:01', addr2='00:11:00:02:00:00')
|
self.addLink(switch[0], router[2], intfName2='r2-eth0', addr1='80:AA:00:00:00:01', addr2='00:11:00:02:00:00')
|
||||||
# Second switch
|
# Second switch
|
||||||
switch[1] = self.addSwitch('sw1', cls=LegacySwitch)
|
switch[1] = self.addSwitch('sw1', cls=topotest.LegacySwitch)
|
||||||
self.addLink(switch[1], router[2], intfName2='r2-eth1', addr1='80:AA:00:01:00:00', addr2='00:11:00:02:00:01')
|
self.addLink(switch[1], router[2], intfName2='r2-eth1', addr1='80:AA:00:01:00:00', addr2='00:11:00:02:00:01')
|
||||||
self.addLink(switch[1], router[3], intfName2='r3-eth0', addr1='80:AA:00:01:00:01', addr2='00:11:00:03:00:00')
|
self.addLink(switch[1], router[3], intfName2='r3-eth0', addr1='80:AA:00:01:00:01', addr2='00:11:00:03:00:00')
|
||||||
self.addLink(switch[1], router[4], intfName2='r4-eth0', addr1='80:AA:00:01:00:02', addr2='00:11:00:04:00:00')
|
self.addLink(switch[1], router[4], intfName2='r4-eth0', addr1='80:AA:00:01:00:02', addr2='00:11:00:04:00:00')
|
||||||
# Third switch
|
# Third switch
|
||||||
switch[2] = self.addSwitch('sw2', cls=LegacySwitch)
|
switch[2] = self.addSwitch('sw2', cls=topotest.LegacySwitch)
|
||||||
self.addLink(switch[2], router[2], intfName2='r2-eth2', addr1='80:AA:00:02:00:00', addr2='00:11:00:02:00:02')
|
self.addLink(switch[2], router[2], intfName2='r2-eth2', addr1='80:AA:00:02:00:00', addr2='00:11:00:02:00:02')
|
||||||
self.addLink(switch[2], router[3], intfName2='r3-eth1', addr1='80:AA:00:02:00:01', addr2='00:11:00:03:00:01')
|
self.addLink(switch[2], router[3], intfName2='r3-eth1', addr1='80:AA:00:02:00:01', addr2='00:11:00:03:00:01')
|
||||||
|
|
||||||
|
|
||||||
#####################################################
|
#####################################################
|
||||||
##
|
##
|
||||||
## Tests starting
|
## Tests starting
|
||||||
@ -292,6 +119,7 @@ class NetworkTopo(Topo):
|
|||||||
|
|
||||||
def setup_module(module):
|
def setup_module(module):
|
||||||
global topo, net
|
global topo, net
|
||||||
|
global fatal_error
|
||||||
|
|
||||||
print("\n\n** %s: Setup Topology" % module.__name__)
|
print("\n\n** %s: Setup Topology" % module.__name__)
|
||||||
print("******************************************\n")
|
print("******************************************\n")
|
||||||
@ -310,7 +138,10 @@ def setup_module(module):
|
|||||||
net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i))
|
net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i))
|
||||||
net['r%s' % i].loadConf('ospfd', '%s/r%s/ospfd.conf' % (thisDir, i))
|
net['r%s' % i].loadConf('ospfd', '%s/r%s/ospfd.conf' % (thisDir, i))
|
||||||
net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i))
|
net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i))
|
||||||
net['r%s' % i].startQuagga()
|
fatal_error = net['r%s' % i].startRouter()
|
||||||
|
|
||||||
|
if fatal_error != "":
|
||||||
|
break
|
||||||
|
|
||||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||||
# CLI(net)
|
# CLI(net)
|
||||||
@ -325,7 +156,7 @@ def teardown_module(module):
|
|||||||
net.stop()
|
net.stop()
|
||||||
|
|
||||||
|
|
||||||
def test_quagga_running():
|
def test_router_running():
|
||||||
global fatal_error
|
global fatal_error
|
||||||
global net
|
global net
|
||||||
|
|
||||||
@ -339,8 +170,11 @@ def test_quagga_running():
|
|||||||
|
|
||||||
# Starting Routers
|
# Starting Routers
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
net['r%s' % i].checkQuaggaRunning()
|
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||||
|
assert fatal_error == "", fatal_error
|
||||||
|
|
||||||
|
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||||
|
# CLI(net)
|
||||||
|
|
||||||
def test_mpls_interfaces():
|
def test_mpls_interfaces():
|
||||||
global fatal_error
|
global fatal_error
|
||||||
@ -383,6 +217,9 @@ def test_mpls_interfaces():
|
|||||||
else:
|
else:
|
||||||
print("r%s ok" % i)
|
print("r%s ok" % i)
|
||||||
|
|
||||||
|
if failures>0:
|
||||||
|
fatal_error = "MPLS LDP Interface status failed"
|
||||||
|
|
||||||
assert failures == 0, "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff)
|
assert failures == 0, "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff)
|
||||||
|
|
||||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||||
|
0
tests/topotests/lib/__init__.py
Normal file
0
tests/topotests/lib/__init__.py
Normal file
232
tests/topotests/lib/topotest.py
Normal file
232
tests/topotests/lib/topotest.py
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
#
|
||||||
|
# topotest.py
|
||||||
|
# Library of helper functions for NetDEF Topology Tests
|
||||||
|
#
|
||||||
|
# Copyright (c) 2016 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
import StringIO
|
||||||
|
import subprocess
|
||||||
|
import platform
|
||||||
|
|
||||||
|
from mininet.topo import Topo
|
||||||
|
from mininet.net import Mininet
|
||||||
|
from mininet.node import Node, OVSSwitch, Host
|
||||||
|
from mininet.log import setLogLevel, info
|
||||||
|
from mininet.cli import CLI
|
||||||
|
from mininet.link import Intf
|
||||||
|
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
def int2dpid(dpid):
|
||||||
|
"Converting Integer to DPID"
|
||||||
|
|
||||||
|
try:
|
||||||
|
dpid = hex(dpid)[2:]
|
||||||
|
dpid = '0'*(16-len(dpid))+dpid
|
||||||
|
return dpid
|
||||||
|
except IndexError:
|
||||||
|
raise Exception('Unable to derive default datapath ID - '
|
||||||
|
'please either specify a dpid or use a '
|
||||||
|
'canonical switch name such as s23.')
|
||||||
|
|
||||||
|
def addRouter(topo, name):
|
||||||
|
"Adding a FreeRangeRouter (or Quagga) to Topology"
|
||||||
|
|
||||||
|
MyPrivateDirs = ['/etc/frr',
|
||||||
|
'/etc/quagga',
|
||||||
|
'/var/run/frr',
|
||||||
|
'/var/run/quagga',
|
||||||
|
'/var/log']
|
||||||
|
return topo.addNode(name, cls=Router, privateDirs=MyPrivateDirs)
|
||||||
|
|
||||||
|
class LinuxRouter(Node):
|
||||||
|
"A Node with IPv4/IPv6 forwarding enabled."
|
||||||
|
|
||||||
|
def config(self, **params):
|
||||||
|
super(LinuxRouter, self).config(**params)
|
||||||
|
# Enable forwarding on the router
|
||||||
|
self.cmd('sysctl net.ipv4.ip_forward=1')
|
||||||
|
self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
|
||||||
|
def terminate(self):
|
||||||
|
"""
|
||||||
|
Terminate generic LinuxRouter Mininet instance
|
||||||
|
"""
|
||||||
|
self.cmd('sysctl net.ipv4.ip_forward=0')
|
||||||
|
self.cmd('sysctl net.ipv6.conf.all.forwarding=0')
|
||||||
|
super(LinuxRouter, self).terminate()
|
||||||
|
|
||||||
|
class Router(Node):
|
||||||
|
"A Node with IPv4/IPv6 forwarding enabled and Quagga as Routing Engine"
|
||||||
|
|
||||||
|
def config(self, **params):
|
||||||
|
super(Router, self).config(**params)
|
||||||
|
|
||||||
|
# Check if Quagga or FRR is installed
|
||||||
|
if os.path.isfile('/usr/lib/frr/zebra'):
|
||||||
|
self.routertype = 'frr'
|
||||||
|
elif os.path.isfile('/usr/lib/quagga/zebra'):
|
||||||
|
self.routertype = 'quagga'
|
||||||
|
else:
|
||||||
|
raise Exception('No FRR or Quagga found in ususal location')
|
||||||
|
# Enable forwarding on the router
|
||||||
|
self.cmd('sysctl net.ipv4.ip_forward=1')
|
||||||
|
self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
|
||||||
|
# Enable coredumps
|
||||||
|
self.cmd('sysctl kernel.core_uses_pid=1')
|
||||||
|
self.cmd('sysctl fs.suid_dumpable=2')
|
||||||
|
self.cmd("sysctl kernel.core_pattern=/tmp/%s_%%e_core-sig_%%s-pid_%%p.dmp" % self.name)
|
||||||
|
self.cmd('ulimit -c unlimited')
|
||||||
|
# Set ownership of config files
|
||||||
|
self.cmd('chown %s:%svty /etc/%s' % (self.routertype, self.routertype, self.routertype))
|
||||||
|
self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0,
|
||||||
|
'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0,
|
||||||
|
'ldpd': 0}
|
||||||
|
def terminate(self):
|
||||||
|
# Delete Running Quagga or FRR Daemons
|
||||||
|
rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype)
|
||||||
|
for d in StringIO.StringIO(rundaemons):
|
||||||
|
self.cmd('kill -7 `cat %s`' % d.rstrip())
|
||||||
|
self.waitOutput()
|
||||||
|
# Disable forwarding
|
||||||
|
self.cmd('sysctl net.ipv4.ip_forward=0')
|
||||||
|
self.cmd('sysctl net.ipv6.conf.all.forwarding=0')
|
||||||
|
super(Router, self).terminate()
|
||||||
|
def removeIPs(self):
|
||||||
|
for interface in self.intfNames():
|
||||||
|
self.cmd('ip address flush', interface)
|
||||||
|
def loadConf(self, daemon, source=None):
|
||||||
|
# print "Daemons before:", self.daemons
|
||||||
|
if daemon in self.daemons.keys():
|
||||||
|
self.daemons[daemon] = 1
|
||||||
|
if source is None:
|
||||||
|
self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon))
|
||||||
|
self.waitOutput()
|
||||||
|
else:
|
||||||
|
self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon))
|
||||||
|
self.waitOutput()
|
||||||
|
self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon))
|
||||||
|
self.waitOutput()
|
||||||
|
self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon))
|
||||||
|
self.waitOutput()
|
||||||
|
else:
|
||||||
|
print("No daemon %s known" % daemon)
|
||||||
|
# print "Daemons after:", self.daemons
|
||||||
|
def startRouter(self):
|
||||||
|
# Disable integrated-vtysh-config
|
||||||
|
with open('/etc/%s/vtysh.conf' % self.routertype, "w") as vtyshfile:
|
||||||
|
vtyshfile.write('no service integrated-vtysh-config')
|
||||||
|
self.cmd('chown %s:%svty /etc/%s/vtysh.conf' % (self.routertype, self.routertype, self.routertype))
|
||||||
|
# Try to find relevant old logfiles in /tmp and delete them
|
||||||
|
map(os.remove, glob.glob("/tmp/*%s*.log" % self.name))
|
||||||
|
# Remove old core files
|
||||||
|
map(os.remove, glob.glob("/tmp/%s*.dmp" % self.name))
|
||||||
|
# Remove IP addresses from OS first - we have them in zebra.conf
|
||||||
|
self.removeIPs()
|
||||||
|
# If ldp is used, check for LDP to be compiled and Linux Kernel to be 4.5 or higher
|
||||||
|
# No error - but return message and skip all the tests
|
||||||
|
if self.daemons['ldpd'] == 1:
|
||||||
|
if not os.path.isfile('/usr/lib/%s/ldpd' % self.routertype):
|
||||||
|
print("LDP Test, but no ldpd compiled or installed")
|
||||||
|
return "LDP Test, but no ldpd compiled or installed"
|
||||||
|
kernel_version = re.search(r'([0-9]+\.[0-9]+).*', platform.release())
|
||||||
|
if kernel_version:
|
||||||
|
if float(kernel_version.group(1)) < 4.5:
|
||||||
|
print("LDP Test need Linux Kernel 4.5 minimum")
|
||||||
|
return "LDP Test need Linux Kernel 4.5 minimum"
|
||||||
|
# Add mpls modules to kernel if we use LDP
|
||||||
|
if self.daemons['ldpd'] == 1:
|
||||||
|
self.cmd('/sbin/modprobe mpls-router')
|
||||||
|
self.cmd('/sbin/modprobe mpls-iptunnel')
|
||||||
|
self.cmd('echo 100000 > /proc/sys/net/mpls/platform_labels')
|
||||||
|
# Start Zebra first
|
||||||
|
if self.daemons['zebra'] == 1:
|
||||||
|
self.cmd('/usr/lib/%s/zebra -d' % self.routertype)
|
||||||
|
self.waitOutput()
|
||||||
|
print('%s: %s zebra started' % (self, self.routertype))
|
||||||
|
sleep(1)
|
||||||
|
# Fix Link-Local Addresses
|
||||||
|
# Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
|
||||||
|
self.cmd('for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=\':\'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done')
|
||||||
|
# Now start all the other daemons
|
||||||
|
for daemon in self.daemons:
|
||||||
|
if (self.daemons[daemon] == 1) and (daemon != 'zebra'):
|
||||||
|
self.cmd('/usr/lib/%s/%s -d' % (self.routertype, daemon))
|
||||||
|
self.waitOutput()
|
||||||
|
print('%s: %s %s started' % (self, self.routertype, daemon))
|
||||||
|
return ""
|
||||||
|
def checkRouterRunning(self):
|
||||||
|
global fatal_error
|
||||||
|
|
||||||
|
daemonsRunning = self.cmd('vtysh -c "show log" | grep "Logging configuration for"')
|
||||||
|
for daemon in self.daemons:
|
||||||
|
if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning):
|
||||||
|
sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon))
|
||||||
|
# Look for core file
|
||||||
|
corefiles = glob.glob("/tmp/%s_%s_core*.dmp" % (self.name, daemon))
|
||||||
|
if (len(corefiles) > 0):
|
||||||
|
backtrace = subprocess.check_output(["gdb /usr/lib/%s/%s %s --batch -ex bt 2> /dev/null" % (self.routertype, daemon, corefiles[0])], shell=True)
|
||||||
|
sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon))
|
||||||
|
sys.stderr.write("%s\n" % backtrace)
|
||||||
|
else:
|
||||||
|
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
|
||||||
|
if os.path.isfile("/tmp/%s-%s.log" % (self.name, daemon)):
|
||||||
|
log_tail = subprocess.check_output(["tail -n20 /tmp/%s-%s.log 2> /dev/null" % (self.name, daemon)], shell=True)
|
||||||
|
sys.stderr.write("\nFrom %s %s %s log file:\n" % (self.routertype, self.name, daemon))
|
||||||
|
sys.stderr.write("%s\n" % log_tail)
|
||||||
|
|
||||||
|
return "%s: Daemon %s not running" % (self.name, daemon)
|
||||||
|
return ""
|
||||||
|
def get_ipv6_linklocal(self):
|
||||||
|
"Get LinkLocal Addresses from interfaces"
|
||||||
|
|
||||||
|
linklocal = []
|
||||||
|
|
||||||
|
ifaces = self.cmd('ip -6 address')
|
||||||
|
# Fix newlines (make them all the same)
|
||||||
|
ifaces = ('\n'.join(ifaces.splitlines()) + '\n').splitlines()
|
||||||
|
interface=""
|
||||||
|
ll_per_if_count=0
|
||||||
|
for line in ifaces:
|
||||||
|
m = re.search('[0-9]+: ([^:@]+)[@if0-9:]+ <', line)
|
||||||
|
if m:
|
||||||
|
interface = m.group(1)
|
||||||
|
ll_per_if_count = 0
|
||||||
|
m = re.search('inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link', line)
|
||||||
|
if m:
|
||||||
|
local = m.group(1)
|
||||||
|
ll_per_if_count += 1
|
||||||
|
if (ll_per_if_count > 1):
|
||||||
|
linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
|
||||||
|
else:
|
||||||
|
linklocal += [[interface, local]]
|
||||||
|
return linklocal
|
||||||
|
|
||||||
|
class LegacySwitch(OVSSwitch):
|
||||||
|
"A Legacy Switch without OpenFlow"
|
||||||
|
|
||||||
|
def __init__(self, name, **params):
|
||||||
|
OVSSwitch.__init__(self, name, failMode='standalone', **params)
|
||||||
|
self.switchIP = None
|
||||||
|
|
@ -34,7 +34,7 @@ test_ospf6_topo1.py:
|
|||||||
| ::1 | ::2 |
|
| ::1 | ::2 |
|
||||||
+---------+---------+ +---------+---------+ |
|
+---------+---------+ +---------+---------+ |
|
||||||
| R1 | | R2 | |
|
| R1 | | R2 | |
|
||||||
| Quagga | | Quagga | |
|
| FreeRangeRouting | | FreeRangeRouting | |
|
||||||
| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
|
| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
|
||||||
+---------+---------+ +---------+---------+ |
|
+---------+---------+ +---------+---------+ |
|
||||||
| ::1 | ::2 \
|
| ::1 | ::2 \
|
||||||
@ -50,7 +50,7 @@ test_ospf6_topo1.py:
|
|||||||
| ::3 | SW3 - Stub Net 3 |
|
| ::3 | SW3 - Stub Net 3 |
|
||||||
+---------+---------+ /-+ fc00:3:3:3::/64 |
|
+---------+---------+ /-+ fc00:3:3:3::/64 |
|
||||||
| R3 | / | /
|
| R3 | / | /
|
||||||
| Quagga +--/ \---- /
|
| FreeRangeRouting +--/ \---- /
|
||||||
| Rtr-ID: 10.0.0.3 | ::3 ___________/
|
| Rtr-ID: 10.0.0.3 | ::3 ___________/
|
||||||
+---------+---------+ \
|
+---------+---------+ \
|
||||||
| ::3 \
|
| ::3 \
|
||||||
@ -64,196 +64,53 @@ test_ospf6_topo1.py:
|
|||||||
| ::4 /
|
| ::4 /
|
||||||
+---------+---------+ /---- |
|
+---------+---------+ /---- |
|
||||||
| R4 | | SW4 - Stub Net 4 |
|
| R4 | | SW4 - Stub Net 4 |
|
||||||
| Quagga +------+ fc00:4:4:4::/64 |
|
| FreeRangeRouting +------+ fc00:4:4:4::/64 |
|
||||||
| Rtr-ID: 10.0.0.4 | ::4 | /
|
| Rtr-ID: 10.0.0.4 | ::4 | /
|
||||||
+-------------------+ \---- /
|
+-------------------+ \---- /
|
||||||
-----/
|
-----/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# import os
|
||||||
|
# import re
|
||||||
|
# import sys
|
||||||
|
# import difflib
|
||||||
|
# import StringIO
|
||||||
|
# import glob
|
||||||
|
# import subprocess
|
||||||
|
|
||||||
|
# from mininet.topo import Topo
|
||||||
|
# from mininet.net import Mininet
|
||||||
|
# from mininet.node import Node, OVSSwitch, Host
|
||||||
|
# from mininet.log import setLogLevel, info
|
||||||
|
# from mininet.cli import CLI
|
||||||
|
|
||||||
|
# from functools import partial
|
||||||
|
# from time import sleep
|
||||||
|
|
||||||
|
# import pytest
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import difflib
|
import difflib
|
||||||
import StringIO
|
import pytest
|
||||||
import glob
|
from time import sleep
|
||||||
import subprocess
|
|
||||||
|
|
||||||
from mininet.topo import Topo
|
from mininet.topo import Topo
|
||||||
from mininet.net import Mininet
|
from mininet.net import Mininet
|
||||||
from mininet.node import Node, OVSSwitch, Host
|
from mininet.node import Node, OVSSwitch, Host
|
||||||
from mininet.log import setLogLevel, info
|
from mininet.log import setLogLevel, info
|
||||||
from mininet.cli import CLI
|
from mininet.cli import CLI
|
||||||
|
from mininet.link import Intf
|
||||||
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
import pytest
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
from lib import topotest
|
||||||
|
|
||||||
|
|
||||||
fatal_error = ""
|
fatal_error = ""
|
||||||
|
|
||||||
def int2dpid(dpid):
|
|
||||||
"Converting Integer to DPID"
|
|
||||||
|
|
||||||
try:
|
|
||||||
dpid = hex(dpid)[2:]
|
|
||||||
dpid = '0'*(16-len(dpid))+dpid
|
|
||||||
return dpid
|
|
||||||
except IndexError:
|
|
||||||
raise Exception('Unable to derive default datapath ID - '
|
|
||||||
'please either specify a dpid or use a '
|
|
||||||
'canonical switch name such as s23.')
|
|
||||||
|
|
||||||
class LinuxRouter(Node):
|
|
||||||
"A Node with IPv4/IPv6 forwarding enabled."
|
|
||||||
|
|
||||||
def config(self, **params):
|
|
||||||
super(LinuxRouter, self).config(**params)
|
|
||||||
# Enable forwarding on the router
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=1')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
|
|
||||||
def terminate(self):
|
|
||||||
"""
|
|
||||||
Terminate generic LinuxRouter Mininet instance
|
|
||||||
"""
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=0')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=0')
|
|
||||||
super(LinuxRouter, self).terminate()
|
|
||||||
|
|
||||||
class QuaggaRouter(Node):
|
|
||||||
"A Node with IPv4/IPv6 forwarding enabled and FRR/Quagga as Routing Engine"
|
|
||||||
|
|
||||||
def config(self, **params):
|
|
||||||
super(QuaggaRouter, self).config(**params)
|
|
||||||
# Check if Quagga or FRR is installed
|
|
||||||
if os.path.isfile('/usr/lib/frr/zebra'):
|
|
||||||
self.routertype = 'frr'
|
|
||||||
elif os.path.isfile('/usr/lib/quagga/zebra'):
|
|
||||||
self.routertype = 'quagga'
|
|
||||||
else:
|
|
||||||
raise Exception('No FRR or Quagga found in ususal location')
|
|
||||||
# Enable forwarding on the router
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=1')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=1')
|
|
||||||
# Enable coredumps
|
|
||||||
self.cmd('sysctl kernel.core_uses_pid=1')
|
|
||||||
self.cmd('sysctl fs.suid_dumpable=2')
|
|
||||||
self.cmd("sysctl kernel.core_pattern=/tmp/%s_%%e_core-sig_%%s-pid_%%p.dmp" % self.name)
|
|
||||||
self.cmd('ulimit -c unlimited')
|
|
||||||
# Set ownership of config files
|
|
||||||
self.cmd('chown %s:%svty /etc/%s' % (self.routertype, self.routertype, self.routertype))
|
|
||||||
self.daemons = {'zebra': 0, 'ripd': 0, 'ripngd': 0, 'ospfd': 0,
|
|
||||||
'ospf6d': 0, 'isisd': 0, 'bgpd': 0, 'pimd': 0}
|
|
||||||
def terminate(self):
|
|
||||||
# Delete Running Quagga Daemons
|
|
||||||
rundaemons = self.cmd('ls -1 /var/run/%s/*.pid' % self.routertype)
|
|
||||||
for d in StringIO.StringIO(rundaemons):
|
|
||||||
self.cmd('kill -7 `cat %s`' % d.rstrip())
|
|
||||||
self.waitOutput()
|
|
||||||
# Disable forwarding
|
|
||||||
self.cmd('sysctl net.ipv4.ip_forward=0')
|
|
||||||
self.cmd('sysctl net.ipv6.conf.all.forwarding=0')
|
|
||||||
super(QuaggaRouter, self).terminate()
|
|
||||||
def removeIPs(self):
|
|
||||||
for interface in self.intfNames():
|
|
||||||
self.cmd('ip address flush', interface)
|
|
||||||
def loadConf(self, daemon, source=None):
|
|
||||||
# print "Daemons before:", self.daemons
|
|
||||||
if daemon in self.daemons.keys():
|
|
||||||
self.daemons[daemon] = 1
|
|
||||||
if source is None:
|
|
||||||
self.cmd('touch /etc/%s/%s.conf' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
else:
|
|
||||||
self.cmd('cp %s /etc/%s/%s.conf' % (source, self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
self.cmd('chmod 640 /etc/%s/%s.conf' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
self.cmd('chown %s:%s /etc/%s/%s.conf' % (self.routertype, self.routertype, self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
else:
|
|
||||||
print("No daemon %s known" % daemon)
|
|
||||||
# print "Daemons after:", self.daemons
|
|
||||||
def startQuagga(self):
|
|
||||||
# Disable integrated-vtysh-config
|
|
||||||
### self.cmd('echo "no service integrated-vtysh-config" > /etc/%s/vtysh.conf' % self.routertype)
|
|
||||||
with open('/etc/%s/vtysh.conf' % self.routertype, "w") as vtyshfile:
|
|
||||||
vtyshfile.write('no service integrated-vtysh-config')
|
|
||||||
self.cmd('chown %s:%svty /etc/%s/vtysh.conf' % (self.routertype, self.routertype, self.routertype))
|
|
||||||
# Try to find relevant old logfiles in /tmp and delete them
|
|
||||||
map(os.remove, glob.glob("/tmp/*%s*.log" % self.name))
|
|
||||||
# Remove old core files
|
|
||||||
map(os.remove, glob.glob("/tmp/%s*.dmp" % self.name))
|
|
||||||
# Remove IP addresses from OS first - we have them in zebra.conf
|
|
||||||
self.removeIPs()
|
|
||||||
# Start Zebra first
|
|
||||||
if self.daemons['zebra'] == 1:
|
|
||||||
self.cmd('/usr/lib/%s/zebra -d' % self.routertype)
|
|
||||||
self.waitOutput()
|
|
||||||
print('%s: %s zebra started' % (self, self.routertype))
|
|
||||||
sleep(1)
|
|
||||||
# Fix Link-Local Addresses
|
|
||||||
# Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
|
|
||||||
self.cmd('for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; IFS=\':\'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done')
|
|
||||||
# Now start all the other daemons
|
|
||||||
for daemon in self.daemons:
|
|
||||||
if (self.daemons[daemon] == 1) and (daemon != 'zebra'):
|
|
||||||
self.cmd('/usr/lib/%s/%s -d' % (self.routertype, daemon))
|
|
||||||
self.waitOutput()
|
|
||||||
print('%s: %s %s started' % (self, self.routertype, daemon))
|
|
||||||
def checkQuaggaRunning(self):
|
|
||||||
global fatal_error
|
|
||||||
|
|
||||||
daemonsRunning = self.cmd('vtysh -c "show log" | grep "Logging configuration for"')
|
|
||||||
failed = []
|
|
||||||
for daemon in self.daemons:
|
|
||||||
if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning):
|
|
||||||
sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon))
|
|
||||||
# Look for core file
|
|
||||||
corefiles = glob.glob("/tmp/%s_%s_core*.dmp" % (self.name, daemon))
|
|
||||||
if (len(corefiles) > 0):
|
|
||||||
backtrace = subprocess.check_output(["gdb /usr/lib/%s/%s %s --batch -ex bt 2> /dev/null" % (self.routertype, daemon, corefiles[0])], shell=True)
|
|
||||||
sys.stderr.write("\n%s: %s crashed. Core file found - Backtrace follows:\n" % (self.name, daemon))
|
|
||||||
sys.stderr.write("%s\n" % backtrace)
|
|
||||||
else:
|
|
||||||
# No core found - If we find matching logfile in /tmp, then print last 20 lines from it.
|
|
||||||
if os.path.isfile("/tmp/%s-%s.log" % (self.name, daemon)):
|
|
||||||
log_tail = subprocess.check_output(["tail -n20 /tmp/%s-%s.log 2> /dev/null" % (self.name, daemon)], shell=True)
|
|
||||||
sys.stderr.write("\nFrom %s %s %s log file:\n" % (self.routertype, self.name, daemon))
|
|
||||||
sys.stderr.write("%s\n" % log_tail)
|
|
||||||
failed += [daemon]
|
|
||||||
return failed
|
|
||||||
def get_ipv6_linklocal(self):
|
|
||||||
"Get LinkLocal Addresses from interfaces"
|
|
||||||
|
|
||||||
linklocal = []
|
|
||||||
|
|
||||||
ifaces = self.cmd('ip -6 address')
|
|
||||||
# Fix newlines (make them all the same)
|
|
||||||
ifaces = ('\n'.join(ifaces.splitlines()) + '\n').splitlines()
|
|
||||||
interface=""
|
|
||||||
ll_per_if_count=0
|
|
||||||
for line in ifaces:
|
|
||||||
m = re.search('[0-9]+: ([^:@]+)[@if0-9:]+ <', line)
|
|
||||||
if m:
|
|
||||||
interface = m.group(1)
|
|
||||||
ll_per_if_count = 0
|
|
||||||
m = re.search('inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link', line)
|
|
||||||
if m:
|
|
||||||
local = m.group(1)
|
|
||||||
ll_per_if_count += 1
|
|
||||||
if (ll_per_if_count > 1):
|
|
||||||
linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
|
|
||||||
else:
|
|
||||||
linklocal += [[interface, local]]
|
|
||||||
return linklocal
|
|
||||||
|
|
||||||
|
|
||||||
class LegacySwitch(OVSSwitch):
|
|
||||||
"A Legacy Switch without OpenFlow"
|
|
||||||
|
|
||||||
def __init__(self, name, **params):
|
|
||||||
OVSSwitch.__init__(self, name, failMode='standalone', **params)
|
|
||||||
self.switchIP = None
|
|
||||||
|
|
||||||
#####################################################
|
#####################################################
|
||||||
##
|
##
|
||||||
@ -262,29 +119,24 @@ class LegacySwitch(OVSSwitch):
|
|||||||
#####################################################
|
#####################################################
|
||||||
|
|
||||||
class NetworkTopo(Topo):
|
class NetworkTopo(Topo):
|
||||||
"A Quagga Topology with direct peering router and IXP connection"
|
"OSPFv3 (IPv6) Test Topology 1"
|
||||||
|
|
||||||
def build(self, **_opts):
|
def build(self, **_opts):
|
||||||
|
|
||||||
quaggaPrivateDirs = ['/etc/quagga',
|
|
||||||
'/etc/frr',
|
|
||||||
'/var/run/quagga',
|
|
||||||
'/var/run/frr',
|
|
||||||
'/var/log']
|
|
||||||
#
|
#
|
||||||
# Define Switches first
|
# Define Switches first
|
||||||
#
|
#
|
||||||
switch = {}
|
switch = {}
|
||||||
for i in range(1, 7):
|
for i in range(1, 7):
|
||||||
switch[i] = self.addSwitch('SW%s' % i, dpid=int2dpid(i),
|
switch[i] = self.addSwitch('SW%s' % i,
|
||||||
cls=LegacySwitch)
|
dpid=topotest.int2dpid(i),
|
||||||
|
cls=topotest.LegacySwitch)
|
||||||
#
|
#
|
||||||
# Define FRR/Quagga Routers
|
# Define FRR/Quagga Routers
|
||||||
#
|
#
|
||||||
router = {}
|
router = {}
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
router[i] = self.addNode('r%s' % i, cls=QuaggaRouter,
|
router[i] = topotest.addRouter(self, 'r%s' % i)
|
||||||
privateDirs=quaggaPrivateDirs)
|
|
||||||
#
|
#
|
||||||
# Wire up the switches and routers
|
# Wire up the switches and routers
|
||||||
#
|
#
|
||||||
@ -328,7 +180,7 @@ def setup_module(module):
|
|||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i))
|
net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i))
|
||||||
net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf' % (thisDir, i))
|
net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf' % (thisDir, i))
|
||||||
net['r%s' % i].startQuagga()
|
net['r%s' % i].startRouter()
|
||||||
|
|
||||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||||
# CLI(net)
|
# CLI(net)
|
||||||
@ -344,7 +196,7 @@ def teardown_module(module):
|
|||||||
net.stop()
|
net.stop()
|
||||||
|
|
||||||
|
|
||||||
def test_quagga_running():
|
def test_router_running():
|
||||||
global fatal_error
|
global fatal_error
|
||||||
global net
|
global net
|
||||||
|
|
||||||
@ -359,13 +211,11 @@ def test_quagga_running():
|
|||||||
# CLI(net)
|
# CLI(net)
|
||||||
failedRunning = ""
|
failedRunning = ""
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
failedDaemon = net['r%s' % i].checkQuaggaRunning()
|
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||||
if failedDaemon:
|
assert fatal_error == "", fatal_error
|
||||||
failedRunning += " Daemons failed on r%s: %s\n" % (i, failedDaemon)
|
|
||||||
if failedRunning:
|
|
||||||
fatal_error = "Some Daemons failed to start or crashed"
|
|
||||||
assert False, "Daemons failed to start or crashed:\n%s" % failedRunning
|
|
||||||
|
|
||||||
|
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||||
|
# CLI(net)
|
||||||
|
|
||||||
def test_ospf6_converged():
|
def test_ospf6_converged():
|
||||||
global fatal_error
|
global fatal_error
|
||||||
|
Loading…
Reference in New Issue
Block a user