Add test cases to test subscriber multi-table support with ONOS using CPQD and a new ONOS app.
This would verify single channel multiple subscriber joins using cpqd.
ONOS multi-table support was found to be broken with cordmcast.
Hence a new app similar to cordmcast was added to verify if multi-table rules were provisioned with ONOS
cpqd pipeline driver.
Using a different subscriber port per subscriber and provisioning an uplink port to be used
for IGMP traffic, it was found that ONOS cpqd driver fails to provision multi-table flows using group ids.
The subscriberMultiTable tests uses the new cordigmp multitable app to provision multicast flows in ONOS
enabling the spring-open-cpqd pipeline driver with ONOS when using CPQD.

If you want to verify/run the subscriberMultiTable test, first rebuild the test container with cpqd using:
sudo cord-test.py build test
And then run the subscriberMultiTable test: test_subscriber_join_recv using:
cord-test.run run --radius -t subscriberMultiTable:subscriber_exchange.test_subscriber_join_recv -k
diff --git a/src/test/utils/OltConfig.py b/src/test/utils/OltConfig.py
index a9fa3c3..bb78563 100644
--- a/src/test/utils/OltConfig.py
+++ b/src/test/utils/OltConfig.py
@@ -56,6 +56,40 @@
         else:
             return None
 
+    def olt_port_map_multi(self):
+        if self.on_olt() and self.olt_conf.has_key('port_map'):
+            port_map = {}
+            if self.olt_conf['port_map'].has_key('ports'):
+                port_map['ports'] = self.olt_conf['port_map']['ports']
+            else:
+                port_map['ports'] = []
+                num_ports = int(self.olt_conf['port_map']['num_ports'])
+                port_map['port'] = self.olt_conf['port_map']['port']
+                for port in xrange(0, num_ports, 2):
+                    port_map['ports'].append('veth{}'.format(port))
+            port_num = 1
+            port_map['uplink'] = int(self.olt_conf['uplink'])
+            port_list = []
+            ##build the port map and inverse port map
+            for port in port_map['ports']:
+                port_map[port_num] = port
+                port_map[port] = port_num
+                if port_num != port_map['uplink']:
+                    ##create tx,rx map
+                    port_list.append( (port_map['uplink'], port_num ) )
+                port_num += 1
+            port_map['start_vlan'] = 0
+            if self.olt_conf['port_map'].has_key('host'):
+                port_map['host'] = self.olt_conf['port_map']['host']
+            else:
+                port_map['host'] = 'ovsbr0'
+            if self.olt_conf['port_map'].has_key('start_vlan'):
+                port_map['start_vlan'] = int(self.olt_conf['port_map']['start_vlan'])
+
+            return port_map, port_list
+        else:
+            return None, None
+
     def olt_device_data(self):
         if self.on_olt():
             accessDeviceDict = {}
diff --git a/src/test/utils/OnosCtrl.py b/src/test/utils/OnosCtrl.py
index 91d5586..880e8f6 100644
--- a/src/test/utils/OnosCtrl.py
+++ b/src/test/utils/OnosCtrl.py
@@ -95,3 +95,12 @@
                                    params = params, headers = headers,
                                    data = payload)
         return result.ok, result.status_code
+
+    @classmethod
+    def uninstall_app(cls, app_name, onos_ip = None):
+        params = {'activate':'true'}
+        headers = {'content-type':'application/octet-stream'}
+        url = cls.applications_url if onos_ip is None else 'http://{0}:8181/onos/v1/applications'.format(onos_ip)
+        app_url = '{}/{}'.format(url, app_name)
+        resp = requests.delete(app_url, auth = cls.auth)
+        return resp.ok, resp.status_code