Merge "Outline and 1st pass on 1st complex case"
diff --git a/olt-complex.py b/olt-complex.py
new file mode 100644
index 0000000..bf4486d
--- /dev/null
+++ b/olt-complex.py
@@ -0,0 +1,108 @@
+'''
+More complex (composite) test-cases for the OLT
+'''
+
+import logging
+from oftest.testutils import *
+from oltbase import OltBaseTest
+
+class Thing(object):
+    """An object we can stash arbitrary attributes for easy reach"""
+    def __init__(self, **kws):
+        self.__dict__.update(kws)
+
+
+class ControllerAccess(OltBaseTest):
+    """Verify openflow access from OLT device"""
+
+    def runTest(self):
+        logging.info("Running ControllerAccess test")
+
+        # implicitly testing access to the openflow device
+        # and deleting all residual flows and groups
+        delete_all_flows(self.controller)
+        delete_all_groups(self.controller)
+
+
+class TestScenario1SingleOnu(OltBaseTest):
+    """
+    Run a comprehensive test scenrario on the OLT.
+
+    Plan:
+
+    1.  Reset the OLT into a clean state
+    2.  Setup the OLT to pass authentication (L2/Unicast) traffic for the ONU1
+    3.  Verify that unicast traffic works in both directions (use a few hundred frames in both ways)
+    4.  Setup IGMP forwarding toward the controller
+    5.  Send periodic IGMP queries out toward the ONU and verify its arrival
+    6.  Send in an IGMP join request for one channel (specific multicast address) and verify that
+        the controller receives it.
+    7.  Setup flows for forwarding multicast traffic from the OLT port toward the ONU(s)
+    8.  Verify that multicast packets can reach the ONU
+    9.  Verify that bidirectional unicast traffic still works across the PON
+    10. Change channel to a new multicast group and verify both multicast receiption as well as
+        unicast traffic works. This step involves:
+        - sending a leave message by the ONU to the controller and verify its reception
+        - sending a join message and verify its reception
+        - removing the existing multicast flow
+        - adding a new multicast flow
+        - verify that original flow no longer flows
+        - verify new flow
+        - verify that unicast still works
+    11. Add a second channel while keeping the existing one. Verify all what needs to be verified
+        (similar as above)
+    12. Add two more multicast channels, and verify everything
+    13. Flip a channel for one of the multicast channels, and verify everything
+    14. Tear down the test.
+    """
+
+    def runTest(self):
+        logging.info("Running %s" % self.__class__.__name__)
+
+        # Some constants
+        c_vlan_id = 111
+        mcast_groups = Thing(
+            ch1="230.10.10.10",
+            ch2="231.11.11.11",
+            ch3="232.12.12.12",
+            ch4="233.13.13.13"
+        )
+        onu1 = Thing(
+            ip="13.14.14.13",
+            mac="b6:b8:3e:fb:1a:3f",
+            s_vlan_id=13
+        )
+
+        # 1.  Reset the OLT into a clean state
+        self.resetOlt()
+
+        # 2.  Setup the OLT to pass authentication (L2/Unicast) traffic for the ONU1
+        self.installDoubleTaggingRules(onu1.s_vlan_id, c_vlan_id, self.getCookieBlock())
+
+        # 3.  Verify that unicast traffic works in both directions (use a few hundred frames in both ways)
+        self.testPacketFlow(onu1.s_vlan_id, c_vlan_id)  # this tests just one packet in each way
+        self.testSustainedPacketFlow(onu1.s_vlan_id, c_vlan_id, 100)
+
+        # 4.  Setup IGMP forwarding toward the controller
+        # 5.  Send periodic IGMP queries out toward the ONU and verify its arrival
+        # 6.  Send in an IGMP join request for one channel (specific multicast address) and verify that
+        #     the controller receives it.
+        # 7.  Setup flows for forwarding multicast traffic from the OLT port toward the ONU(s)
+        # 8.  Verify that multicast packets can reach the ONU
+        # 9.  Verify that bidirectional unicast traffic still works across the PON
+        # 10. Change channel to a new multicast group and verify both multicast receiption as well as
+        #     unicast traffic works. This step involves:
+        #     - sending a leave message by the ONU to the controller and verify its reception
+        #     - sending a join message and verify its reception
+        #     - removing the existing multicast flow
+        #     - adding a new multicast flow
+        #     - verify that original flow no longer flows
+        #     - verify new flow
+        #     - verify that unicast still works
+        # 11. Add a second channel while keeping the existing one. Verify all what needs to be verified
+        #     (similar as above)
+        # 12. Add two more multicast channels, and verify everything
+        # 13. Flip a channel for one of the multicast channels, and verify everything
+
+        # 14. Tear down the test.
+        self.resetOlt()
diff --git a/oltbase.py b/oltbase.py
index fff3740..6ff4bc4 100644
--- a/oltbase.py
+++ b/oltbase.py
@@ -9,6 +9,20 @@
 
 class OltBaseTest(base_tests.SimpleDataPlane):
 
+    next_cookie_block = 40
+
+    def getCookieBlock(self):
+        """Returns the starting value of the next 100 cookies"""
+        c = self.next_cookie_block
+        OltBaseTest.next_cookie_block += 100
+        return c
+
+    def resetOlt(self):
+        """Reset the OLT into a clean healthy state"""
+        delete_all_flows(self.controller)
+        do_barrier(self.controller)
+        verify_no_errors(self.controller)
+
     def testPacketIn(self, match, parsed_pkt):
         delete_all_flows(self.controller)
 
@@ -73,16 +87,21 @@
         self.controller.message_send(request)
         do_barrier(self.controller)
 
-    def testPacketFlow(self, s_vlan_id, c_vlan_id, onu = None):
+    def testPacketFlow(self,
+                       s_vlan_id,
+                       c_vlan_id,
+                       onu=None,
+                       verify_blocked_flows=True,
+                       pktlen=96):
 
-        incorrectTagPkt = simple_udp_packet(pktlen=100, dl_vlan_enable=True, vlan_vid=100, vlan_pcp=1)
-        zeroTaggedPkt = simple_udp_packet(pktlen=100, dl_vlan_enable=True, vlan_vid=0, vlan_pcp=0)
-        untaggedPkt = simple_udp_packet(pktlen=96)
-
-        upstreamDoubleTaggedPkt = double_vlan_udp_packet(pktlen=104, dl_vlan_enable=True,
-                                                         c_vlan_vid=c_vlan_id,
-                                                         s_vlan_vid=s_vlan_id,
-                                                         c_vlan_pcp=0, s_vlan_pcp=0)
+        incorrectTagPkt = simple_udp_packet(
+            pktlen=pktlen+4, dl_vlan_enable=True, vlan_vid=s_vlan_id, vlan_pcp=1)
+        zeroTaggedPkt = simple_udp_packet(
+            pktlen=pktlen+4, dl_vlan_enable=True, vlan_vid=0, vlan_pcp=0)
+        untaggedPkt = simple_udp_packet(pktlen=pktlen)
+        upstreamDoubleTaggedPkt = double_vlan_udp_packet(
+            pktlen=pktlen+8, dl_vlan_enable=True, c_vlan_vid=c_vlan_id, s_vlan_vid=s_vlan_id,
+            c_vlan_pcp=0, s_vlan_pcp=0)
 
         inport = onu_port if onu is None else onu
 
@@ -99,17 +118,23 @@
         else:
             verify_packet(self, untaggedPkt, inport)
 
-        # test upstream doubletagged packet got dropped
-        self.dataplane.send(inport, str(upstreamDoubleTaggedPkt))
-        verify_no_packet(self, upstreamDoubleTaggedPkt, olt_port)
+        if verify_blocked_flows:
+            # test upstream doubletagged packet got dropped
+            self.dataplane.send(inport, str(upstreamDoubleTaggedPkt))
+            verify_no_packet(self, upstreamDoubleTaggedPkt, olt_port)
 
-        # test downstream untagged packet got dropped at ONU
-        self.dataplane.send(olt_port, str(untaggedPkt))
-        verify_no_packet(self, untaggedPkt, inport)
+            # test downstream untagged packet got dropped at ONU
+            self.dataplane.send(olt_port, str(untaggedPkt))
+            verify_no_packet(self, untaggedPkt, inport)
 
-        # test upstream icorrectly tagged packet; should get dropped
-        self.dataplane.send(inport, str(incorrectTagPkt))
-        verify_no_packet(self, upstreamDoubleTaggedPkt, olt_port)
+            # test upstream icorrectly tagged packet; should get dropped
+            self.dataplane.send(inport, str(incorrectTagPkt))
+            verify_no_packet(self, upstreamDoubleTaggedPkt, olt_port)
+
+    def testSustainedPacketFlow(self, s_vlan_id, c_vlan_id, number_of_roundtrips=10, onu=None):
+        for i in xrange(number_of_roundtrips):
+            print "loop %d" % i
+            self.testPacketFlow(s_vlan_id, c_vlan_id, onu=onu, verify_blocked_flows=False)
 
     def installDoubleTaggingRules(self, s_vlan_id, c_vlan_id, cookie=42, onu = None):