Added unit tests to test emulator core
authorpeusterm <manuel.peuster@uni-paderborn.de>
Fri, 15 Jan 2016 11:32:53 +0000 (12:32 +0100)
committerpeusterm <manuel.peuster@uni-paderborn.de>
Fri, 15 Jan 2016 11:32:53 +0000 (12:32 +0100)
README.md
emuvim/test/__main__.py [new file with mode: 0644]
emuvim/test/runner.py [new file with mode: 0644]
emuvim/test/test_api_zerorpc.py [new file with mode: 0644]
emuvim/test/test_emulator.py [new file with mode: 0644]

index 91d75c8..7d75760 100644 (file)
--- a/README.md
+++ b/README.md
@@ -56,9 +56,10 @@ Automatic installation is provide through an Ansible playbook.
 ### TODO
 * DCemulator
  * ...
-* Write unit tests
+
 * Add resource constraints to datacenters
 * Check if we can use the Mininet GUI to visualize our DCs?
+* (Unit tests for zerorpc API endpoint)
 
 
 ### Features / Done
@@ -78,3 +79,4 @@ Automatic installation is provide through an Ansible playbook.
  * Start compute
  * Stop compute
 * Create an Ansible-based automatic installation routine
+* Unit tests
diff --git a/emuvim/test/__main__.py b/emuvim/test/__main__.py
new file mode 100644 (file)
index 0000000..f7fa66d
--- /dev/null
@@ -0,0 +1,7 @@
+import runner
+import os
+
+
+if __name__ == '__main__':
+    thisdir = os.path.dirname( os.path.realpath( __file__ ) )
+    runner.main(thisdir)
diff --git a/emuvim/test/runner.py b/emuvim/test/runner.py
new file mode 100644 (file)
index 0000000..93b0822
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+
+"""
+Run all tests
+ -v : verbose output
+ -e : emulator test only (no API tests)
+ -a : API tests only
+"""
+
+from unittest import defaultTestLoader, TextTestRunner, TestSuite
+import os
+import sys
+from mininet.util import ensureRoot
+from mininet.clean import cleanup
+from mininet.log import setLogLevel
+
+
+def runTests( testDir, verbosity=1, emuonly=False, apionly=False ):
+    "discover and run all tests in testDir"
+    # ensure root and cleanup before starting tests
+    ensureRoot()
+    cleanup()
+    # discover all tests in testDir
+    testSuite = defaultTestLoader.discover( testDir )
+    if emuonly:
+        testSuiteFiltered = [s for s in testSuite if "Emulator" in str(s)]
+        testSuite = TestSuite()
+        testSuite.addTests(testSuiteFiltered)
+    if apionly:
+        testSuiteFiltered = [s for s in testSuite if "Api" in str(s)]
+        testSuite = TestSuite()
+        testSuite.addTests(testSuiteFiltered)
+
+    # run tests
+    TextTestRunner( verbosity=verbosity ).run( testSuite )
+
+
+def main(thisdir):
+    setLogLevel( 'warning' )
+    # get the directory containing example tests
+    vlevel = 2 if '-v' in sys.argv else 1
+    emuonly = ('-e' in sys.argv)
+    apionly = ('-a' in sys.argv)
+    runTests(
+        testDir=thisdir, verbosity=vlevel, emuonly=emuonly, apionly=apionly)
+
+
+if __name__ == '__main__':
+    thisdir = os.path.dirname( os.path.realpath( __file__ ) )
+    main(thisdir)
diff --git a/emuvim/test/test_api_zerorpc.py b/emuvim/test/test_api_zerorpc.py
new file mode 100644 (file)
index 0000000..2830872
--- /dev/null
@@ -0,0 +1 @@
+#TODO we'll need this at some time. But I'am lazy. A good REST API seems to be more important.
diff --git a/emuvim/test/test_emulator.py b/emuvim/test/test_emulator.py
new file mode 100644 (file)
index 0000000..7da7aaf
--- /dev/null
@@ -0,0 +1,351 @@
+"""
+Test suite to automatically test emulator functionalities.
+Directly interacts with the emulator through the Mininet-like
+Python API.
+
+Does not test API endpoints. This is done in separated test suites.
+"""
+
+import unittest
+import os
+import time
+import subprocess
+import docker
+from dcemulator.net import DCNetwork
+from dcemulator.node import EmulatorCompute
+from mininet.node import Host, Controller, OVSSwitch, Docker
+from mininet.link import TCLink
+from mininet.topo import SingleSwitchTopo, LinearTopo
+from mininet.log import setLogLevel
+from mininet.util import quietRun
+from mininet.clean import cleanup
+
+
+class simpleTestTopology( unittest.TestCase ):
+    """
+        Helper class to do basic test setups.
+        s1 -- s2 -- s3 -- ... -- sN
+    """
+
+    def __init__(self, *args, **kwargs):
+        self.net = None
+        self.s = []   # list of switches
+        self.h = []   # list of hosts
+        self.d = []   # list of docker containers
+        self.dc = []  # list of data centers
+        self.docker_cli = None
+        super(simpleTestTopology, self).__init__(*args, **kwargs)
+
+    def createNet(
+            self,
+            nswitches=0, ndatacenter=0, nhosts=0, ndockers=0,
+            autolinkswitches=False):
+        """
+        Creates a Mininet instance and automatically adds some
+        nodes to it.
+        """
+        self.net = net = DCNetwork()
+
+        # add some switches
+        for i in range(0, nswitches):
+            self.s.append(self.net.addSwitch('s%d' % i))
+        # if specified, chain all switches
+        if autolinkswitches:
+            for i in range(0, len(self.s) - 1):
+                self.net.addLink(self.s[i], self.s[i + 1])
+        # add some data centers
+        for i in range(0, ndatacenter):
+            self.dc.append(self.net.addDatacenter('dc%d' % i))
+        # add some hosts
+        for i in range(0, nhosts):
+            self.h.append(self.net.addHost('h%d' % i))
+        # add some dockers
+        for i in range(0, ndockers):
+            self.d.append(self.net.addDocker('d%d' % i, dimage="ubuntu"))
+
+    def startNet(self):
+        self.net.start()
+
+    def stopNet(self):
+        self.net.stop()
+
+    def getDockerCli(self):
+        """
+        Helper to interact with local docker instance.
+        """
+        if self.docker_cli is None:
+            self.docker_cli = docker.Client(
+                base_url='unix://var/run/docker.sock')
+        return self.docker_cli
+
+    @staticmethod
+    def setUp():
+        pass
+
+    @staticmethod
+    def tearDown():
+        cleanup()
+        # make sure that all pending docker containers are killed
+        with open(os.devnull, 'w') as devnull:
+            subprocess.call(
+                "sudo docker rm -f $(sudo docker ps -a -q)",
+                stdout=devnull,
+                stderr=devnull,
+                shell=True)
+
+
+#@unittest.skip("disabled topology tests for development")
+class testEmulatorTopology( simpleTestTopology ):
+    """
+    Tests to check the topology API of the emulator.
+    """
+
+    def testSingleDatacenter(self):
+        """
+        Create a single data center and add check if its switch is up
+        by using manually added hosts. Tests especially the
+        data center specific addLink method.
+        """
+        # create network
+        self.createNet(nswitches=0, ndatacenter=1, nhosts=2, ndockers=0)
+        # setup links
+        self.net.addLink(self.dc[0], self.h[0])
+        self.net.addLink(self.h[1], self.dc[0])
+        # start Mininet network
+        self.startNet()
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 0)
+        assert(len(self.net.hosts) == 2)
+        assert(len(self.net.switches) == 1)
+        # check connectivity by using ping
+        assert(self.net.ping([self.h[0], self.h[1]]) <= 0.0)
+        # stop Mininet network
+        self.stopNet()
+
+    def testMultipleDatacenterDirect(self):
+        """
+        Create a two data centers and interconnect them.
+        """
+        # create network
+        self.createNet(nswitches=0, ndatacenter=2, nhosts=2, ndockers=0)
+        # setup links
+        self.net.addLink(self.dc[0], self.h[0])
+        self.net.addLink(self.h[1], self.dc[1])
+        self.net.addLink(self.dc[0], self.dc[1])
+        # start Mininet network
+        self.startNet()
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 0)
+        assert(len(self.net.hosts) == 2)
+        assert(len(self.net.switches) == 2)
+        # check connectivity by using ping
+        assert(self.net.ping([self.h[0], self.h[1]]) <= 0.0)
+        # stop Mininet network
+        self.stopNet()
+
+    def testMultipleDatacenterWithIntermediateSwitches(self):
+        """
+        Create a two data centers and interconnect them with additional
+        switches between them.
+        """
+        # create network
+        self.createNet(
+            nswitches=3, ndatacenter=2, nhosts=2, ndockers=0,
+            autolinkswitches=True)
+        # setup links
+        self.net.addLink(self.dc[0], self.h[0])
+        self.net.addLink(self.h[1], self.dc[1])
+        self.net.addLink(self.dc[0], self.s[0])
+        self.net.addLink(self.s[2], self.dc[1])
+        # start Mininet network
+        self.startNet()
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 0)
+        assert(len(self.net.hosts) == 2)
+        assert(len(self.net.switches) == 5)
+        # check connectivity by using ping
+        assert(self.net.ping([self.h[0], self.h[1]]) <= 0.0)
+        # stop Mininet network
+        self.stopNet()
+
+
+#@unittest.skip("disabled compute tests for development")
+class testEmulatorCompute( simpleTestTopology ):
+    """
+    Tests to check the emulator's API to add and remove
+    compute resources at runtime.
+    """
+
+    def testAddSingleComputeSingleDC(self):
+        """
+        Adds a single compute instance to
+        a single DC and checks its connectivity with a
+        manually added host.
+        """
+        # create network
+        self.createNet(nswitches=0, ndatacenter=1, nhosts=1, ndockers=0)
+        # setup links
+        self.net.addLink(self.dc[0], self.h[0])
+        # start Mininet network
+        self.startNet()
+        # add compute resources
+        vnf1 = self.dc[0].startCompute("vnf1")
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 1)
+        assert(len(self.net.hosts) == 2)
+        assert(len(self.net.switches) == 1)
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 1)
+        assert(isinstance(self.dc[0].listCompute()[0], EmulatorCompute))
+        assert(self.dc[0].listCompute()[0].name == "vnf1")
+        # check connectivity by using ping
+        assert(self.net.ping([self.h[0], vnf1]) <= 0.0)
+        # stop Mininet network
+        self.stopNet()
+
+    def testRemoveSingleComputeSingleDC(self):
+        """
+        Test stop method for compute instances.
+        Check that the instance is really removed.
+        """
+        # create network
+        self.createNet(nswitches=0, ndatacenter=1, nhosts=1, ndockers=0)
+        # setup links
+        self.net.addLink(self.dc[0], self.h[0])
+        # start Mininet network
+        self.startNet()
+        # add compute resources
+        vnf1 = self.dc[0].startCompute("vnf1")
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 1)
+        assert(len(self.net.hosts) == 2)
+        assert(len(self.net.switches) == 1)
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 1)
+        # check connectivity by using ping
+        assert(self.net.ping([self.h[0], vnf1]) <= 0.0)
+        # remove compute resources
+        self.dc[0].stopCompute("vnf1")
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 0)
+        assert(len(self.net.hosts) == 1)
+        assert(len(self.net.switches) == 1)
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 0)
+        # stop Mininet network
+        self.stopNet()
+
+    def testGetStatusSingleComputeSingleDC(self):
+        """
+        Check if the getStatus functionality of EmulatorCompute
+        objects works well.
+        """
+        # create network
+        self.createNet(nswitches=0, ndatacenter=1, nhosts=1, ndockers=0)
+        # setup links
+        self.net.addLink(self.dc[0], self.h[0])
+        # start Mininet network
+        self.startNet()
+        # add compute resources
+        vnf1 = self.dc[0].startCompute("vnf1")
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 1)
+        assert(len(self.net.hosts) == 2)
+        assert(len(self.net.switches) == 1)
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 1)
+        assert(isinstance(self.dc[0].listCompute()[0], EmulatorCompute))
+        assert(self.dc[0].listCompute()[0].name == "vnf1")
+        # check connectivity by using ping
+        assert(self.net.ping([self.h[0], vnf1]) <= 0.0)
+        # check get status
+        s = self.dc[0].containers.get("vnf1").getStatus()
+        assert(s["name"] == "vnf1")
+        assert(s["state"]["Running"])
+        # stop Mininet network
+        self.stopNet()
+
+    def testConnectivityMultiDC(self):
+        """
+        Test if compute instances started in different data centers
+        are able to talk to each other.
+        """
+        # create network
+        self.createNet(
+            nswitches=3, ndatacenter=2, nhosts=0, ndockers=0,
+            autolinkswitches=True)
+        # setup links
+        self.net.addLink(self.dc[0], self.s[0])
+        self.net.addLink(self.dc[1], self.s[2])
+        # start Mininet network
+        self.startNet()
+        # add compute resources
+        vnf1 = self.dc[0].startCompute("vnf1")
+        vnf2 = self.dc[1].startCompute("vnf2")
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 2)
+        assert(len(self.net.hosts) == 2)
+        assert(len(self.net.switches) == 5)
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 1)
+        assert(len(self.dc[1].listCompute()) == 1)
+        # check connectivity by using ping
+        assert(self.net.ping([vnf1, vnf2]) <= 0.0)
+        # stop Mininet network
+        self.stopNet()
+
+    def testInterleavedAddRemoveMultiDC(self):
+        """
+        Test multiple, interleaved add and remove operations and ensure
+        that always all expected compute instances are reachable.
+        """
+                # create network
+        self.createNet(
+            nswitches=3, ndatacenter=2, nhosts=0, ndockers=0,
+            autolinkswitches=True)
+        # setup links
+        self.net.addLink(self.dc[0], self.s[0])
+        self.net.addLink(self.dc[1], self.s[2])
+        # start Mininet network
+        self.startNet()
+        # add compute resources
+        vnf1 = self.dc[0].startCompute("vnf1")
+        vnf2 = self.dc[1].startCompute("vnf2")
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 2)
+        assert(len(self.net.hosts) == 2)
+        assert(len(self.net.switches) == 5)
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 1)
+        assert(len(self.dc[1].listCompute()) == 1)
+        # check connectivity by using ping
+        assert(self.net.ping([vnf1, vnf2]) <= 0.0)
+        # remove compute resources
+        self.dc[0].stopCompute("vnf1")
+        # check number of running nodes
+        assert(len(self.getDockerCli().containers()) == 1)
+        assert(len(self.net.hosts) == 1)
+        assert(len(self.net.switches) == 5)
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 0)
+        assert(len(self.dc[1].listCompute()) == 1)
+        # add compute resources
+        vnf3 = self.dc[0].startCompute("vnf3")
+        vnf4 = self.dc[0].startCompute("vnf4")
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 2)
+        assert(len(self.dc[1].listCompute()) == 1)
+        assert(self.net.ping([vnf3, vnf2]) <= 0.0)
+        assert(self.net.ping([vnf4, vnf2]) <= 0.0)
+        # remove compute resources
+        self.dc[0].stopCompute("vnf3")
+        self.dc[0].stopCompute("vnf4")
+        self.dc[1].stopCompute("vnf2")
+        # check compute list result
+        assert(len(self.dc[0].listCompute()) == 0)
+        assert(len(self.dc[1].listCompute()) == 0)
+        # stop Mininet network
+        self.stopNet()
+
+if __name__ == '__main__':
+    unittest.main()