Sunday, May 12, 2013

PyTapDEMon - Part 4 Unittest for the Network

Here are links to the first three parts of the project:


For part 4, I will attempt to write some testing for the project. Of course, as part of the TDD approach, this really should have been either part 2 of the project, or be integrated into all the steps during the development. But how many of us really write the tests first? So here I am, trying to write some tests. :)

To be honest, in my opinion, this is part of the larger problem that it is hard to write tests against code that either make changes to the network or monitors the network. The network is inherently distributed and traditionally been stateless from one another. Such as the case here, for the test, I can write test to test my code; or I can write test to query the network and see if it is in an expected state. If I have time, I should do both, but comparing the two, I have decided to write test for the network state because ultimately that is what I care about the most. 

The objectives are simple: 

1. Query the switches for the current flow information. 
2. Compare them to the expected port-based flow data. 

I am going to use ovs-ofctl as the OOB channel to query the OpenFlow switch data. This is what the flows should look like on s1, for example, when the flows are installed:

mininet@mininet-vm:~$ sudo ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
 cookie=0x0, duration=116.257s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=3 actions=output:4
 cookie=0x0, duration=116.259s, table=0, n_packets=3, n_bytes=238, idle_timeout=120,hard_timeout=120,in_port=1 actions=output:2,output:6,output:8
 cookie=0x0, duration=116.255s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=4 actions=output:3
 cookie=0x0, duration=116.258s, table=0, n_packets=3, n_bytes=238, idle_timeout=120,hard_timeout=120,in_port=2 actions=output:1,output:6,output:8
mininet@mininet-vm:~$

And here is what the output would look like when there are no flows: 

mininet@mininet-vm:~$ sudo ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
mininet@mininet-vm:~$


Here is the unittest code for the tests: 

***
#!/usr/bin/env python

import unittest
import subprocess

def parseFlows(flows):
    """
    Parse out the string representation of flows passed in.
    Example:
    NXST_FLOW reply (xid=0x4):
     cookie=0x0, duration=4.329s, table=0, n_packets=0, n_bytes=0, idle_timeout=120,hard_timeout=120,in_port=3 actions=output:4
    """
    switchFlows = {}
    for flow in flows.split('\n'):
        line = flow.split()
        if len(line) > 3: #get rid of first line in flow output
            inputPort = line[5].split(',')[2].split('=')[1]
            outputPorts = line[6].split('actions=')[1]
            switchFlows[inputPort] = outputPorts
    return switchFlows
    

globalFlows = {}
for i in range(1, 4):
    """Query switches s1, s2, s3 and dump flows, add to global flow dictionary"""
    switch = 's'+str(i)
    flows = subprocess.check_output(['sudo', 'ovs-ofctl', 'dump-flows', switch])
    switchFlows = parseFlows(flows)
    globalFlows[switch] = switchFlows


class PyTapDEMON_Test(unittest.TestCase):
    def test_s1_port1(self):
        self.assertEqual('output:2,output:6,output:8', globalFlows['s1']['1'])
   
    def test_s2_port1(self):
        self.assertEqual('output:2,output:6,output:8', globalFlows['s2']['1'])

    def test_s3_port10(self):
        self.assertEqual('output:11', globalFlows['s3']['10'])


if __name__ == '__main__':
    unittest.main()

***

Here is the output during passing state: 

1. Generate the packet to install flows in the switches: 

mininet> h1 ping -c3 h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_req=2 ttl=64 time=0.329 ms
64 bytes from 10.0.0.2: icmp_req=3 ttl=64 time=0.106 ms

--- 10.0.0.2 ping statistics ---
3 packets transmitted, 2 received, 33% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.106/0.217/0.329/0.112 ms
mininet>

2. Run the test: 

mininet@mininet-vm:~/PyTapDEMON/tests$ pytest PyTapDEMON_Test.py
========================  PyTapDEMON_Test.py  ========================
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s

OK
*******************************************************************************

mininet@mininet-vm:~/PyTapDEMON/tests$

*** 

Here is the output during failing state (no flow installed):

1. Verified that there is no flow in s1: 

mininet@mininet-vm:~$ sudo ovs-ofctl dump-flows s1
NXST_FLOW reply (xid=0x4):
mininet@mininet-vm:~$

2. Run the test: 

mininet@mininet-vm:~/PyTapDEMON/tests$ pytest PyTapDEMON_Test.py
========================  PyTapDEMON_Test.py  ========================
EEE
======================================================================
ERROR: test_s1_port1 (PyTapDEMON_Test.PyTapDEMON_Test)
----------------------------------------------------------------------
Traceback (most recent call last)
  File "/usr/lib/python2.7/unittest/case.py", line 332, in run
    testMethod()
  File "/home/mininet/PyTapDEMON/tests/PyTapDEMON_Test.py", line 34, in test_s1_port1
    self.assertEqual('output:2,output:6,output:8', globalFlows['s1']['1'])
KeyError: '1'

                              no stdout                              
                              no stderr                              
======================================================================
ERROR: test_s2_port1 (PyTapDEMON_Test.PyTapDEMON_Test)
----------------------------------------------------------------------
Traceback (most recent call last)
  File "/usr/lib/python2.7/unittest/case.py", line 332, in run
    testMethod()
  File "/home/mininet/PyTapDEMON/tests/PyTapDEMON_Test.py", line 37, in test_s2_port1
    self.assertEqual('output:2,output:6,output:8', globalFlows['s2']['1'])
KeyError: '1'

                              no stdout                              
                              no stderr                              
======================================================================
ERROR: test_s3_port10 (PyTapDEMON_Test.PyTapDEMON_Test)
----------------------------------------------------------------------
Traceback (most recent call last)
  File "/usr/lib/python2.7/unittest/case.py", line 332, in run
    testMethod()
  File "/home/mininet/PyTapDEMON/tests/PyTapDEMON_Test.py", line 40, in test_s3_port10
    self.assertEqual('output:11', globalFlows['s3']['10'])
KeyError: '10'

                              no stdout                              
                              no stderr                              
----------------------------------------------------------------------
Ran 3 tests in 0.041s

FAILED (errors=3)
*******************************************************************************

mininet@mininet-vm:~/PyTapDEMON/tests$

***

It is pretty easy to write 'unittest' for your network in an OpenFlow enabled network, in this example I query the switches individually, but in theory I should interface directly with the controller. 

Cheers. Leave me comments and let me know what you think. 




No comments:

Post a Comment