mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-16 08:32:45 +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
|
||||
__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 |
|
||||
@ -47,7 +47,7 @@ test_bgp_multiview_topo1.py: Simple Quagga Route-Server Test
|
||||
|
|
||||
| .254
|
||||
+---------+---------+
|
||||
| Quagga R1 |
|
||||
| FRR R1 |
|
||||
| BGP Multi-View |
|
||||
| Peer 1-3 > View 1 |
|
||||
| Peer 4-5 > View 2 |
|
||||
@ -66,9 +66,8 @@ import os
|
||||
import re
|
||||
import sys
|
||||
import difflib
|
||||
import StringIO
|
||||
import glob
|
||||
import subprocess
|
||||
import pytest
|
||||
from time import sleep
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
@ -78,151 +77,12 @@ from mininet.cli import CLI
|
||||
from mininet.link import Intf
|
||||
|
||||
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 = ""
|
||||
|
||||
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):
|
||||
"A LinuxRouter connecting three IP subnets"
|
||||
"BGP Multiview Topology 1"
|
||||
|
||||
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
|
||||
quagga = {}
|
||||
router = {}
|
||||
for i in range(1, 2):
|
||||
quagga[i] = self.addNode('r%s' % i, cls=QuaggaRouter,
|
||||
privateDirs=quaggaPrivateDirs)
|
||||
router[i] = topotest.addRouter(self, 'r%s' % i)
|
||||
|
||||
# Setup Provider BGP peers
|
||||
peer = {}
|
||||
@ -260,11 +114,11 @@ class NetworkTopo(Topo):
|
||||
# Setup Switches
|
||||
switch = {}
|
||||
# First switch is for a dummy interface (for local network)
|
||||
switch[0] = self.addSwitch('sw0', cls=LegacySwitch)
|
||||
self.addLink(switch[0], quagga[1], intfName2='r1-stub')
|
||||
switch[0] = self.addSwitch('sw0', cls=topotest.LegacySwitch)
|
||||
self.addLink(switch[0], router[1], intfName2='r1-stub')
|
||||
# Second switch is for connection to all peering routers
|
||||
switch[1] = self.addSwitch('sw1', cls=LegacySwitch)
|
||||
self.addLink(switch[1], quagga[1], intfName2='r1-eth0')
|
||||
switch[1] = self.addSwitch('sw1', cls=topotest.LegacySwitch)
|
||||
self.addLink(switch[1], router[1], intfName2='r1-eth0')
|
||||
for j in range(1, 9):
|
||||
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):
|
||||
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].startQuagga()
|
||||
net['r%s' % i].startRouter()
|
||||
|
||||
# Starting PE Hosts and init ExaBGP on each of them
|
||||
print('*** Starting BGP on all 8 Peers in 10s')
|
||||
@ -309,7 +163,7 @@ def setup_module(module):
|
||||
print('peer%s' % i),
|
||||
print('')
|
||||
|
||||
# For debugging after starting Quagga daemons, uncomment the next line
|
||||
# For debugging after starting Quagga/FRR daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
def teardown_module(module):
|
||||
@ -327,7 +181,7 @@ def teardown_module(module):
|
||||
# End - Shutdown network
|
||||
net.stop()
|
||||
|
||||
def test_quagga_running():
|
||||
def test_router_running():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
@ -339,15 +193,14 @@ def test_quagga_running():
|
||||
print("******************************************\n")
|
||||
sleep(5)
|
||||
|
||||
# CLI(net)
|
||||
failedRunning = ""
|
||||
# Starting Routers
|
||||
for i in range(1, 2):
|
||||
failedDaemon = net['r%s' % i].checkQuaggaRunning()
|
||||
if failedDaemon:
|
||||
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
|
||||
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_bgp_converge():
|
||||
"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")
|
||||
# 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)
|
||||
|
||||
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)
|
||||
|
||||
# For debugging after starting Quagga daemons, uncomment the next line
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
|
@ -63,10 +63,8 @@ import os
|
||||
import re
|
||||
import sys
|
||||
import difflib
|
||||
import StringIO
|
||||
import glob
|
||||
import subprocess
|
||||
import platform
|
||||
import pytest
|
||||
from time import sleep
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
@ -75,173 +73,11 @@ from mininet.log import setLogLevel, info
|
||||
from mininet.cli import CLI
|
||||
from mininet.link import Intf
|
||||
|
||||
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 = ""
|
||||
|
||||
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
|
||||
@ -249,41 +85,32 @@ class LegacySwitch(OVSSwitch):
|
||||
#####################################################
|
||||
|
||||
class NetworkTopo(Topo):
|
||||
"A LinuxRouter connecting three IP subnets"
|
||||
"LDP Test Topology 1"
|
||||
|
||||
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
|
||||
router = {}
|
||||
for i in range(1, 5):
|
||||
router[i] = self.addNode('r%s' % i, cls=QuaggaRouter,
|
||||
privateDirs=quaggaPrivateDirs)
|
||||
router[i] = topotest.addRouter(self, 'r%s' % i)
|
||||
|
||||
# Setup Switches
|
||||
# Setup Switches, add Interfaces and Connections
|
||||
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[2], intfName2='r2-eth0', addr1='80:AA:00:00:00:01', addr2='00:11:00:02:00:00')
|
||||
# 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[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')
|
||||
# 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[3], intfName2='r3-eth1', addr1='80:AA:00:02:00:01', addr2='00:11:00:03:00:01')
|
||||
|
||||
|
||||
#####################################################
|
||||
##
|
||||
## Tests starting
|
||||
@ -292,6 +119,7 @@ class NetworkTopo(Topo):
|
||||
|
||||
def setup_module(module):
|
||||
global topo, net
|
||||
global fatal_error
|
||||
|
||||
print("\n\n** %s: Setup Topology" % module.__name__)
|
||||
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('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].startQuagga()
|
||||
fatal_error = net['r%s' % i].startRouter()
|
||||
|
||||
if fatal_error != "":
|
||||
break
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
@ -325,7 +156,7 @@ def teardown_module(module):
|
||||
net.stop()
|
||||
|
||||
|
||||
def test_quagga_running():
|
||||
def test_router_running():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
@ -339,8 +170,11 @@ def test_quagga_running():
|
||||
|
||||
# Starting Routers
|
||||
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():
|
||||
global fatal_error
|
||||
@ -383,6 +217,9 @@ def test_mpls_interfaces():
|
||||
else:
|
||||
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)
|
||||
|
||||
# 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 |
|
||||
+---------+---------+ +---------+---------+ |
|
||||
| R1 | | R2 | |
|
||||
| Quagga | | Quagga | |
|
||||
| FreeRangeRouting | | FreeRangeRouting | |
|
||||
| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
|
||||
+---------+---------+ +---------+---------+ |
|
||||
| ::1 | ::2 \
|
||||
@ -50,7 +50,7 @@ test_ospf6_topo1.py:
|
||||
| ::3 | SW3 - Stub Net 3 |
|
||||
+---------+---------+ /-+ fc00:3:3:3::/64 |
|
||||
| R3 | / | /
|
||||
| Quagga +--/ \---- /
|
||||
| FreeRangeRouting +--/ \---- /
|
||||
| Rtr-ID: 10.0.0.3 | ::3 ___________/
|
||||
+---------+---------+ \
|
||||
| ::3 \
|
||||
@ -64,196 +64,53 @@ test_ospf6_topo1.py:
|
||||
| ::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 | /
|
||||
+-------------------+ \---- /
|
||||
-----/
|
||||
"""
|
||||
|
||||
# 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 re
|
||||
import sys
|
||||
import difflib
|
||||
import StringIO
|
||||
import glob
|
||||
import subprocess
|
||||
import pytest
|
||||
from time import sleep
|
||||
|
||||
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 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 = ""
|
||||
|
||||
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):
|
||||
"A Quagga Topology with direct peering router and IXP connection"
|
||||
"OSPFv3 (IPv6) Test Topology 1"
|
||||
|
||||
def build(self, **_opts):
|
||||
|
||||
quaggaPrivateDirs = ['/etc/quagga',
|
||||
'/etc/frr',
|
||||
'/var/run/quagga',
|
||||
'/var/run/frr',
|
||||
'/var/log']
|
||||
#
|
||||
# Define Switches first
|
||||
#
|
||||
switch = {}
|
||||
for i in range(1, 7):
|
||||
switch[i] = self.addSwitch('SW%s' % i, dpid=int2dpid(i),
|
||||
cls=LegacySwitch)
|
||||
switch[i] = self.addSwitch('SW%s' % i,
|
||||
dpid=topotest.int2dpid(i),
|
||||
cls=topotest.LegacySwitch)
|
||||
#
|
||||
# Define FRR/Quagga Routers
|
||||
#
|
||||
router = {}
|
||||
for i in range(1, 5):
|
||||
router[i] = self.addNode('r%s' % i, cls=QuaggaRouter,
|
||||
privateDirs=quaggaPrivateDirs)
|
||||
router[i] = topotest.addRouter(self, 'r%s' % i)
|
||||
|
||||
#
|
||||
# Wire up the switches and routers
|
||||
#
|
||||
@ -328,7 +180,7 @@ def setup_module(module):
|
||||
for i in range(1, 5):
|
||||
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].startQuagga()
|
||||
net['r%s' % i].startRouter()
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
@ -344,7 +196,7 @@ def teardown_module(module):
|
||||
net.stop()
|
||||
|
||||
|
||||
def test_quagga_running():
|
||||
def test_router_running():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
@ -359,13 +211,11 @@ def test_quagga_running():
|
||||
# CLI(net)
|
||||
failedRunning = ""
|
||||
for i in range(1, 5):
|
||||
failedDaemon = net['r%s' % i].checkQuaggaRunning()
|
||||
if failedDaemon:
|
||||
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
|
||||
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_ospf6_converged():
|
||||
global fatal_error
|
||||
|
Loading…
Reference in New Issue
Block a user