From: Martin Winter Date: Mon, 30 Jan 2017 21:50:48 +0000 (-0800) Subject: Move common function and class to topotest library X-Git-Tag: frr-7.1-dev~151^2~354 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=594b1259f994f781c19afd98ae5cee034cf3805b;p=mirror%2Ffrr.git Move common function and class to topotest library Signed-off-by: Martin Winter --- diff --git a/tests/topotests/.gitignore b/tests/topotests/.gitignore index b3c9e26dcc..9ab8c1f765 100644 --- a/tests/topotests/.gitignore +++ b/tests/topotests/.gitignore @@ -1,2 +1,3 @@ .cache __pycache__ +*.pyc diff --git a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py index c3165a10a6..4836e2ba06 100755 --- a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py +++ b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py @@ -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) diff --git a/tests/topotests/ldp-topo1/test_ldp_topo1.py b/tests/topotests/ldp-topo1/test_ldp_topo1.py index 81807fa214..88b01d6dbb 100755 --- a/tests/topotests/ldp-topo1/test_ldp_topo1.py +++ b/tests/topotests/ldp-topo1/test_ldp_topo1.py @@ -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 diff --git a/tests/topotests/lib/__init__.py b/tests/topotests/lib/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py new file mode 100644 index 0000000000..175edf4ab7 --- /dev/null +++ b/tests/topotests/lib/topotest.py @@ -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 + diff --git a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py index f669fdecdc..7b3eeb9549 100755 --- a/tests/topotests/ospf6-topo1/test_ospf6_topo1.py +++ b/tests/topotests/ospf6-topo1/test_ospf6_topo1.py @@ -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