]> git.puffer.fish Git - matthieu/frr.git/commitdiff
Move common function and class to topotest library
authorMartin Winter <mwinter@opensourcerouting.org>
Mon, 30 Jan 2017 21:50:48 +0000 (13:50 -0800)
committerDonald Sharp <sharpd@cumulusnetworks.com>
Wed, 28 Nov 2018 01:22:11 +0000 (20:22 -0500)
Signed-off-by: Martin Winter <mwinter@opensourcerouting.org>
tests/topotests/.gitignore
tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py
tests/topotests/ldp-topo1/test_ldp_topo1.py
tests/topotests/lib/__init__.py [new file with mode: 0644]
tests/topotests/lib/topotest.py [new file with mode: 0644]
tests/topotests/ospf6-topo1/test_ospf6_topo1.py

index b3c9e26dcca0adceb3db5c987c1336cfff822549..9ab8c1f7655a907c4b8c1af90076818768f4da6d 100644 (file)
@@ -1,2 +1,3 @@
 .cache
 __pycache__
+*.pyc
index c3165a10a63e7f05264285f8f8ddc9a5f3dfefc7..4836e2ba0623f63053325fb0e154e7330066e500 100755 (executable)
@@ -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)
 
 
index 81807fa21482695fd6e5c52b59c18a2de97a45a2..88b01d6dbbab311be4053d5e942e802a931a01cb 100755 (executable)
@@ -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 (file)
index 0000000..e69de29
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
new file mode 100644 (file)
index 0000000..175edf4
--- /dev/null
@@ -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
+
index f669fdecdc6cecc941bb1d25295429f8f6228d07..7b3eeb9549657c920b08fe9518bb2109bdc3c892 100755 (executable)
@@ -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