Test: Cluster validation scenarios.
It tests for multi-instance ONOS deployment in cord perspective.
Tests detection and handling nodes joining and leaving the clusters and also
checks for primary controller.
Also tests some cluster scenarios in conjunction of cord apps like vrouter, igmp etc.

Change-Id: Id39ba640188b36c60c3e30d8edcb6bdf80db8d69
diff --git a/src/test/utils/Cluster.py b/src/test/utils/Cluster.py
new file mode 100644
index 0000000..bb17d03
--- /dev/null
+++ b/src/test/utils/Cluster.py
@@ -0,0 +1,2514 @@
+import unittest
+from twisted.internet import defer
+from nose.tools import *
+from IGMP import *
+from ACL import ACLTest
+from DHCP import DHCPTest
+from Channels import Channels, IgmpChannel
+from subscriberDb import SubscriberDB
+import time, monotonic
+from OltConfig import OltConfig
+from OnosCtrl import OnosCtrl, get_mac
+from OnosFlowCtrl import OnosFlowCtrl
+from CordContainer import Container, Onos, Quagga
+from onosclidriver import OnosCliDriver
+from CordTestServer import CordTestServer, cord_test_onos_restart, cord_test_quagga_restart,cord_test_quagga_stop, cord_test_quagga_shell,cord_test_shell
+from portmaps import g_subscriber_port_map
+import time, monotonic
+from scapy_ssl_tls.ssl_tls import *
+from scapy_ssl_tls.ssl_tls_crypto import *
+import os
+import json
+from socket import socket
+import pexpect
+from Stats import Stats
+from scapy.all import *
+from OnosFlowCtrl import OnosFlowCtrl
+from nose.twistedtools import reactor, deferred
+import tempfile
+import threading
+from threading import current_thread
+from threadPool import ThreadPool
+import random
+import collections
+import requests
+log.setLevel('INFO')
+
+class cluster_igmp(object):
+    V_INF1 = 'veth0'
+    V_INF2 = 'veth1'
+    MGROUP1 = '239.1.2.3'
+    MGROUP2 = '239.2.2.3'
+    MINVALIDGROUP1 = '255.255.255.255'
+    MINVALIDGROUP2 = '239.255.255.255'
+    MMACGROUP1 = "01:00:5e:01:02:03"
+    MMACGROUP2 = "01:00:5e:02:02:03"
+    IGMP_DST_MAC = "01:00:5e:00:00:16"
+    IGMP_SRC_MAC = "5a:e1:ac:ec:4d:a1"
+    IP_SRC = '1.2.3.4'
+    IP_DST = '224.0.0.22'
+    NEGATIVE_TRAFFIC_STATUS = 1
+    igmp_eth = Ether(dst = IGMP_DST_MAC, type = ETH_P_IP)
+    igmp_ip = IP(dst = IP_DST)
+    IGMP_TEST_TIMEOUT = 5
+    IGMP_QUERY_TIMEOUT = 60
+    MCAST_TRAFFIC_TIMEOUT = 10
+    PORT_TX_DEFAULT = 2
+    PORT_RX_DEFAULT = 1
+    max_packets = 100
+    app = 'org.opencord.igmp'
+    olt_conf_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../setup/olt_config.json')
+    ROVER_TEST_TIMEOUT = 300 #3600*86
+    ROVER_TIMEOUT = (ROVER_TEST_TIMEOUT - 100)
+    ROVER_JOIN_TIMEOUT = 60
+
+    @classmethod
+    def setUpClass(cls):
+          cls.olt = OltConfig(olt_conf_file = cls.olt_conf_file)
+          cls.port_map, _ = cls.olt.olt_port_map()
+          OnosCtrl.cord_olt_config(cls.olt.olt_device_data())
+
+    @classmethod
+    def tearDownClass(cls): pass
+
+    def setUp(self):
+	self.setUpClass()
+	self.get_igmp_intf()
+        ''' Activate the igmp app'''
+        self.onos_ctrl = OnosCtrl(self.app)
+        self.onos_ctrl.activate()
+        self.igmp_channel = IgmpChannel()
+
+    def tearDown(self): pass
+
+    def onos_load_config(self, config):
+        log.info('onos load config is %s'%config)
+        status, code = OnosCtrl.config(config)
+        if status is False:
+            log.info('JSON request returned status %d' %code)
+            assert_equal(status, True)
+        time.sleep(2)
+
+    def onos_ssm_table_load(self, groups, src_list = ['1.2.3.4'],flag = False):
+          ssm_dict = {'apps' : { 'org.onosproject.igmp' : { 'ssmTranslate' : [] } } }
+          ssm_xlate_list = ssm_dict['apps']['org.onosproject.igmp']['ssmTranslate']
+          if flag: #to maintain seperate group-source pair.
+              for i in range(len(groups)):
+                  d = {}
+                  d['source'] = src_list[i] or '0.0.0.0'
+                  d['group'] = groups[i]
+                  ssm_xlate_list.append(d)
+          else:
+              for g in groups:
+                  for s in src_list:
+                      d = {}
+                      d['source'] = s or '0.0.0.0'
+                      d['group'] = g
+                      ssm_xlate_list.append(d)
+          self.onos_load_config(ssm_dict)
+          cord_port_map = {}
+          for g in groups:
+                cord_port_map[g] = (self.PORT_TX_DEFAULT, self.PORT_RX_DEFAULT)
+          self.igmp_channel.cord_port_table_load(cord_port_map)
+          time.sleep(2)
+
+    def mcast_ip_range(self,start_ip = '224.0.1.0', end_ip = '224.0.1.100'):
+        start = list(map(int, start_ip.split(".")))
+        end = list(map(int, end_ip.split(".")))
+        temp = start
+        ip_range = []
+        ip_range.append(start_ip)
+        while temp != end:
+            start[3] += 1
+            for i in (3, 2, 1):
+                if temp[i] == 255:
+                    temp[i] = 0
+                    temp[i-1] += 1
+            ip_range.append(".".join(map(str, temp)))
+        return ip_range
+
+    def random_mcast_ip(self,start_ip = '224.0.1.0', end_ip = '224.0.1.100'):
+        start = list(map(int, start_ip.split(".")))
+        end = list(map(int, end_ip.split(".")))
+        temp = start
+        ip_range = []
+        ip_range.append(start_ip)
+        while temp != end:
+            start[3] += 1
+            for i in (3, 2, 1):
+                if temp[i] == 255:
+                    temp[i] = 0
+                    temp[i-1] += 1
+            ip_range.append(".".join(map(str, temp)))
+        return random.choice(ip_range)
+
+    def source_ip_range(self,start_ip = '10.10.0.1', end_ip = '10.10.0.100'):
+        start = list(map(int, start_ip.split(".")))
+        end = list(map(int, end_ip.split(".")))
+        temp = start
+        ip_range = []
+        ip_range.append(start_ip)
+        while temp != end:
+            start[3] += 1
+            for i in (3, 2, 1):
+                if temp[i] == 255:
+                    temp[i] = 0
+                    temp[i-1] += 1
+            ip_range.append(".".join(map(str, temp)))
+        return ip_range
+    def iptomac(self, mcast_ip):
+        mcast_mac =  '01:00:5e:'
+        octets = mcast_ip.split('.')
+        second_oct = int(octets[1]) & 127
+        third_oct = int(octets[2])
+        fourth_oct = int(octets[3])
+        mcast_mac = mcast_mac + format(second_oct,'02x') + ':' + format(third_oct, '02x') + ':' + format(fourth_oct, '02x')
+        return mcast_mac
+
+    def randomsourceip(self,start_ip = '10.10.0.1', end_ip = '10.10.0.100'):
+        start = list(map(int, start_ip.split(".")))
+        end = list(map(int, end_ip.split(".")))
+        temp = start
+        ip_range = []
+        ip_range.append(start_ip)
+        while temp != end:
+            start[3] += 1
+            for i in (3, 2, 1):
+                if temp[i] == 255:
+                    temp[i] = 0
+                    temp[i-1] += 1
+            ip_range.append(".".join(map(str, temp)))
+        return random.choice(ip_range)
+
+    def get_igmp_intf(self):
+        inst = os.getenv('TEST_INSTANCE', None)
+        if not inst:
+            return 'veth0'
+        inst = int(inst) + 1
+        if inst >= self.port_map['uplink']:
+            inst += 1
+        if self.port_map.has_key(inst):
+              return self.port_map[inst]
+        return 'veth0'
+
+    def igmp_verify_join(self, igmpStateList):
+        sendState, recvState = igmpStateList
+        ## check if the send is received for the groups
+        for g in sendState.groups:
+            tx_stats = sendState.group_map[g][0]
+            tx = tx_stats.count
+            assert_greater(tx, 0)
+            rx_stats = recvState.group_map[g][1]
+            rx = rx_stats.count
+            assert_greater(rx, 0)
+            log.info('Receive stats %s for group %s' %(rx_stats, g))
+
+        log.info('IGMP test verification success')
+
+    def igmp_verify_leave(self, igmpStateList, leave_groups):
+        sendState, recvState = igmpStateList[0], igmpStateList[1]
+        ## check if the send is received for the groups
+        for g in sendState.groups:
+            tx_stats = sendState.group_map[g][0]
+            rx_stats = recvState.group_map[g][1]
+            tx = tx_stats.count
+            rx = rx_stats.count
+            assert_greater(tx, 0)
+            if g not in leave_groups:
+                log.info('Received %d packets for group %s' %(rx, g))
+        for g in leave_groups:
+            rx = recvState.group_map[g][1].count
+            assert_equal(rx, 0)
+
+        log.info('IGMP test verification success')
+
+    def mcast_traffic_timer(self):
+          self.mcastTraffic.stopReceives()
+
+    def send_mcast_cb(self, send_state):
+        for g in send_state.groups:
+            send_state.update(g, tx = 1)
+        return 0
+
+    ##Runs in the context of twisted reactor thread
+    def igmp_recv(self, igmpState, iface = 'veth0'):
+        p = self.recv_socket.recv()
+        try:
+              send_time = float(p.payload.load)
+              recv_time = monotonic.monotonic()
+        except:
+              log.info('Unexpected Payload received: %s' %p.payload.load)
+              return 0
+        #log.info( 'Recv in %.6f secs' %(recv_time - send_time))
+        igmpState.update(p.dst, rx = 1, t = recv_time - send_time)
+        return 0
+
+    def send_igmp_join(self, groups, src_list = ['1.2.3.4'], record_type=IGMP_V3_GR_TYPE_INCLUDE,
+                       ip_pkt = None, iface = 'veth0', ssm_load = False, delay = 1):
+        if ssm_load is True:
+              self.onos_ssm_table_load(groups, src_list)
+        igmp = IGMPv3(type = IGMP_TYPE_V3_MEMBERSHIP_REPORT, max_resp_code=30,
+                      gaddr=self.IP_DST)
+        for g in groups:
+              gr = IGMPv3gr(rtype= record_type, mcaddr=g)
+              gr.sources = src_list
+              igmp.grps.append(gr)
+        if ip_pkt is None:
+              ip_pkt = self.igmp_eth/self.igmp_ip
+        pkt = ip_pkt/igmp
+        IGMPv3.fixup(pkt)
+        sendp(pkt, iface=iface)
+        log.info('igmp join packet is %s'%pkt.show())
+        if delay != 0:
+            time.sleep(delay)
+
+class cluster_tls():
+    eap_app = 'org.opencord.aaa'
+    CLIENT_CERT_INVALID = '''-----BEGIN CERTIFICATE-----
+MIIEyTCCA7GgAwIBAgIJAM6l2jUG56pLMA0GCSqGSIb3DQEBCwUAMIGLMQswCQYD
+VQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVNvbWV3aGVyZTETMBEGA1UE
+ChMKQ2llbmEgSW5jLjEeMBwGCSqGSIb3DQEJARYPYWRtaW5AY2llbmEuY29tMSYw
+JAYDVQQDEx1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0xNjAzMTEx
+ODUzMzVaFw0xNzAzMDYxODUzMzVaMIGLMQswCQYDVQQGEwJVUzELMAkGA1UECBMC
+Q0ExEjAQBgNVBAcTCVNvbWV3aGVyZTETMBEGA1UEChMKQ2llbmEgSW5jLjEeMBwG
+CSqGSIb3DQEJARYPYWRtaW5AY2llbmEuY29tMSYwJAYDVQQDEx1FeGFtcGxlIENl
+cnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAL9Jv54TkqycL3U2Fdd/y5NXdnPVXwAVV3m6I3eIffVCv8eS+mwlbl9dnbjo
+qqlGEgA3sEg5HtnKoW81l3PSyV/YaqzUzbcpDlgWlbNkFQ3nVxh61gSU34Fc4h/W
+plSvCkwGSbV5udLtEe6S9IflP2Fu/eXa9vmUtoPqDk66p9U/nWVf2H1GJy7XanWg
+wke+HpQvbzoSfPJS0e5Rm9KErrzaIkJpqt7soW+OjVJitUax7h45RYY1HHHlbMQ0
+ndWW8UDsCxFQO6d7nsijCzY69Y8HarH4mbVtqhg3KJevxD9UMRy6gdtPMDZLah1c
+LHRu14ucOK4aF8oICOgtcD06auUCAwEAAaOCASwwggEoMB0GA1UdDgQWBBQwEs0m
+c8HARTVp21wtiwgav5biqjCBwAYDVR0jBIG4MIG1gBQwEs0mc8HARTVp21wtiwga
+v5biqqGBkaSBjjCBizELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQH
+EwlTb21ld2hlcmUxEzARBgNVBAoTCkNpZW5hIEluYy4xHjAcBgkqhkiG9w0BCQEW
+D2FkbWluQGNpZW5hLmNvbTEmMCQGA1UEAxMdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBB
+dXRob3JpdHmCCQDOpdo1BueqSzAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6Ap
+oCeGJWh0dHA6Ly93d3cuZXhhbXBsZS5jb20vZXhhbXBsZV9jYS5jcmwwDQYJKoZI
+hvcNAQELBQADggEBAK+fyAFO8CbH35P5mOX+5wf7+AeC+5pwaFcoCV0zlfwniANp
+jISgcIX9rcetLxeYRAO5com3+qLdd9dGVNL0kwufH4QhlSPErG7OLHHAs4JWVhUo
+bH3lK9lgFVlnCDBtQhslzqScR64SCicWcQEjv3ZMZsJwYLvl8unSaKz4+LVPeJ2L
+opCpmZw/V/S2NhBbe3QjTiRPmDev2gbaO4GCfi/6sCDU7UO3o8KryrkeeMIiFIej
+gfwn9fovmpeqCEyupy2JNNUTJibEuFknwx7JAX+htPL27nEgwV1FYtwI3qLiZqkM
+729wo9cFSslJNZBu+GsBP5LszQSuvNTDWytV+qY=
+-----END CERTIFICATE-----'''
+    def __init__(self):
+	pass
+    def setUp(self):
+        self.onos_ctrl = OnosCtrl(self.eap_app)
+        self.onos_aaa_config()
+
+    def onos_aaa_config(self):
+        aaa_dict = {'apps' : { 'org.onosproject.aaa' : { 'AAA' : { 'radiusSecret': 'radius_password',
+                                                                   'radiusIp': '172.17.0.2' } } } }
+        radius_ip = os.getenv('ONOS_AAA_IP') or '172.17.0.2'
+        aaa_dict['apps']['org.onosproject.aaa']['AAA']['radiusIp'] = radius_ip
+        self.onos_ctrl.activate()
+        time.sleep(2)
+        self.onos_load_config(aaa_dict)
+
+    def onos_load_config(self, config):
+        log.info('onos load config is %s'%config)
+        status, code = OnosCtrl.config(config)
+        if status is False:
+            log.info('JSON request returned status %d' %code)
+            assert_equal(status, True)
+        time.sleep(2)
+
+
+class cluster_flows():
+
+    PORT_TX_DEFAULT = 2
+    PORT_RX_DEFAULT = 1
+    INTF_TX_DEFAULT = 'veth2'
+    INTF_RX_DEFAULT = 'veth0'
+    default_port_map = {
+        PORT_TX_DEFAULT : INTF_TX_DEFAULT,
+        PORT_RX_DEFAULT : INTF_RX_DEFAULT,
+        INTF_TX_DEFAULT : PORT_TX_DEFAULT,
+        INTF_RX_DEFAULT : PORT_RX_DEFAULT
+        }
+    app = 'org.onosproject.cli'
+
+    def incmac(self, mac):
+        tmp =  str(hex(int('0x'+mac,16)+1).split('x')[1])
+        mac = '0'+ tmp if len(tmp) < 2 else tmp
+        return mac
+
+    def next_mac(self, mac):
+        mac = mac.split(":")
+        mac[5] = self.incmac(mac[5])
+
+        if len(mac[5]) > 2:
+           mac[0] = self.incmac(mac[0])
+           mac[5] = '01'
+
+        if len(mac[0]) > 2:
+           mac[0] = '01'
+           mac[1] = self.incmac(mac[1])
+           mac[5] = '01'
+        return ':'.join(mac)
+
+    def to_egress_mac(cls, mac):
+        mac = mac.split(":")
+        mac[4] = '01'
+
+        return ':'.join(mac)
+
+    def inc_ip(self, ip, i):
+
+        ip[i] =str(int(ip[i])+1)
+        return '.'.join(ip)
+
+
+    def next_ip(self, ip):
+
+        lst = ip.split('.')
+        for i in (3,0,-1):
+            if int(lst[i]) < 255:
+               return self.inc_ip(lst, i)
+            elif int(lst[i]) == 255:
+               lst[i] = '0'
+               if int(lst[i-1]) < 255:
+                  return self.inc_ip(lst,i-1)
+               elif int(lst[i-2]) < 255:
+                  lst[i-1] = '0'
+                  return self.inc_ip(lst,i-2)
+               else:
+                  break
+
+    def randomip(self,start_ip = '10.10.0.1', end_ip = '10.10.0.100'):
+        start = list(map(int, start_ip.split(".")))
+        end = list(map(int, end_ip.split(".")))
+        temp = start
+        ip_range = []
+        ip_range.append(start_ip)
+        while temp != end:
+            start[3] += 1
+            for i in (3, 2, 1):
+                if temp[i] == 255:
+                    temp[i] = 0
+                    temp[i-1] += 1
+            ip_range.append(".".join(map(str, temp)))
+        return random.choice(ip_range)
+
+    def ip_range(self,start_ip = '10.10.0.1', end_ip = '10.10.0.100'):
+        start = list(map(int, start_ip.split(".")))
+        end = list(map(int, end_ip.split(".")))
+        temp = start
+        ip_range = []
+        ip_range.append(start_ip)
+        while temp != end:
+            start[3] += 1
+            for i in (3, 2, 1):
+                if temp[i] == 255:
+                    temp[i] = 0
+                    temp[i-1] += 1
+            ip_range.append(".".join(map(str, temp)))
+        return ip_range
+
+    def to_egress_ip(self, ip):
+        lst=ip.split('.')
+        lst[0] = '182'
+        return '.'.join(lst)
+
+    @classmethod
+    def setUpClass(cls):
+        cls.olt = OltConfig()
+        cls.port_map, _ = cls.olt.olt_port_map()
+        if not cls.port_map:
+            cls.port_map = cls.default_port_map
+        cls.device_id = OnosCtrl.get_device_id()
+
+class cluster_proxyarp():
+    apps = ('org.onosproject.vrouter','org.onosproject.proxyarp')
+    device_id = 'of:' + get_mac()
+    device_dict = { "devices" : {
+                "{}".format(device_id) : {
+                    "basic" : {
+                        "driver" : "softrouter"
+                    }
+                }
+             },
+          }
+    test_path = os.path.dirname(os.path.realpath(__file__))
+    onos_config_path = os.path.join(test_path, '..', 'setup/onos-config')
+    GATEWAY = '192.168.10.50'
+    INGRESS_PORT = 1
+    EGRESS_PORT = 2
+    MAX_PORTS = 100
+    hosts_list = [ ('192.168.10.1', '00:00:00:00:00:01'), ('192.168.11.1', '00:00:00:00:02:01'), ]
+
+    olt_conf_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), '../setup/olt_config.json')
+
+    @classmethod
+    def setUpClass(cls):
+        cls.olt = OltConfig()
+        cls.port_map, _ = cls.olt.olt_port_map()
+	print('port map in proxyarp setUpClass is %s'%cls.port_map)
+        if not cls.port_map:
+            cls.port_map = g_subscriber_port_map
+        time.sleep(3)
+        cls.load_device_id()
+
+    @classmethod
+    def load_device_id(cls):
+        did = OnosCtrl.get_device_id()
+        cls.device_id = did
+        cls.device_dict = { "devices" : {
+                "{}".format(did) : {
+                    "basic" : {
+                        "driver" : "softrouter"
+                    }
+                }
+            },
+        }
+    def cliEnter(self):
+        retries = 0
+        while retries < 3:
+            self.cli = OnosCliDriver(connect = True)
+            if self.cli.handle:
+                break
+            else:
+                retries += 1
+                time.sleep(2)
+
+    def cliExit(self):
+        self.cli.disconnect()
+
+    @classmethod
+    def interface_config_load(cls, interface_cfg = None):
+        if type(interface_cfg) is tuple:
+            res = []
+            for v in interface_cfg:
+                if type(v) == list:
+                    pass
+                else:
+                    res += v.items()
+                    config = dict(res)
+        else:
+            config = interface_cfg
+        cfg = json.dumps(config)
+        with open('{}/network-cfg.json'.format(cls.onos_config_path), 'w') as f:
+            f.write(cfg)
+        return cord_test_onos_restart(config=config)
+
+    @classmethod
+    def host_config_load(cls, host_config = None):
+        for host in host_config:
+            status, code = OnosCtrl.host_config(host)
+            if status is False:
+                log.info('JSON request returned status %d' %code)
+                assert_equal(status, True)
+
+    @classmethod
+    def generate_interface_config(cls, hosts = 1):
+        num = 0
+        start_host = ( 192 << 24) | ( 168 << 16)  |  (10 << 8) | 0
+        end_host =   ( 200 << 24 ) | (168 << 16)  |  (10 << 8) | 0
+        ports_dict = { 'ports' : {} }
+        interface_list = []
+        hosts_list = []
+        for n in xrange(start_host, end_host, 256):
+            port_map = ports_dict['ports']
+            port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
+            device_port_key = '{0}/{1}'.format(cls.device_id, port)
+            try:
+                interfaces = port_map[device_port_key]['interfaces']
+            except:
+                port_map[device_port_key] = { 'interfaces' : [] }
+                interfaces = port_map[device_port_key]['interfaces']
+            ip = n + 1
+            host_ip = n + 2
+            ips = '%d.%d.%d.%d/24'%( (ip >> 24) & 0xff, ( (ip >> 16) & 0xff ), ( (ip >> 8 ) & 0xff ), ip & 0xff)
+            host = '%d.%d.%d.%d' % ( (host_ip >> 24) & 0xff, ( ( host_ip >> 16) & 0xff ), ( (host_ip >> 8 ) & 0xff ), host_ip & 0xff )
+            mac = RandMAC()._fix()
+            hosts_list.append((host, mac))
+            if num < cls.MAX_PORTS - 1:
+                interface_dict = { 'name' : 'b1-{}'.format(port), 'ips': [ips], 'mac' : mac }
+                interfaces.append(interface_dict)
+                interface_list.append(interface_dict['name'])
+            else:
+                interfaces[0]['ips'].append(ips)
+            num += 1
+            if num == hosts:
+                break
+        cls.hosts_list = hosts_list
+        return (cls.device_dict, ports_dict, hosts_list)
+
+    @classmethod
+    def generate_host_config(cls):
+        num = 0
+        hosts_dict = {}
+        for host, mac in cls.hosts_list:
+            port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
+            hosts_dict[host] = {'mac':mac, 'vlan':'none', 'ipAddresses':[host], 'location':{ 'elementId' : '{}'.format(cls.device_id), 'port': port}}
+            num += 1
+        return hosts_dict.values()
+
+    @classmethod
+    def proxyarp_activate(cls, deactivate = False):
+        app = 'org.onosproject.proxyarp'
+        onos_ctrl = OnosCtrl(app)
+        if deactivate is True:
+            onos_ctrl.deactivate()
+        else:
+            onos_ctrl.activate()
+        time.sleep(3)
+
+    @classmethod
+    def proxyarp_config(cls, hosts = 1):
+        proxyarp_configs = cls.generate_interface_config(hosts = hosts)
+        cls.interface_config_load(interface_cfg = proxyarp_configs)
+        hostcfg = cls.generate_host_config()
+        cls.host_config_load(host_config = hostcfg)
+        return proxyarp_configs
+
+    def proxyarp_arpreply_verify(self, ingress, hostip, hostmac, PositiveTest=True):
+        #log.info('verifying arp reply for host ip %s host mac %s on interface %s'%(hostip ,hostmac ,self.port_map[ingress]))
+        self.success = False
+        def recv_task():
+            def recv_cb(pkt):
+                log.info('Arp Reply seen with source Mac is %s' %(pkt[ARP].hwsrc))
+                self.success = True if PositiveTest == True else False
+            sniff(count=1, timeout=2, lfilter = lambda p: ARP in p and p[ARP].op == 2 and p[ARP].hwsrc == hostmac,
+                  prn = recv_cb, iface = self.port_map[ingress])
+        t = threading.Thread(target = recv_task)
+        t.start()
+        pkt = (Ether(dst = 'ff:ff:ff:ff:ff:ff')/ARP(op=1,pdst=hostip))
+        log.info('sending arp request  for dest ip %s on interface %s' %
+                 (hostip, self.port_map[ingress]))
+        sendp( pkt, count = 10, iface = self.port_map[ingress])
+        t.join()
+        if PositiveTest:
+            assert_equal(self.success, True)
+        else:
+            assert_equal(self.success, False)
+
+    def proxyarp_hosts_verify(self, hosts = 1,PositiveTest = True):
+	log.info('verifying arp reply for host ip host mac on interface %s'%(self.port_map[2]))
+        _,_,hosts_config = self.proxyarp_config(hosts = hosts)
+        log.info('\nhosts_config %s and its type %s'%(hosts_config,type(hosts_config)))
+        self.cliEnter()
+        connected_hosts = json.loads(self.cli.hosts(jsonFormat = True))
+        log.info('Discovered hosts: %s' %connected_hosts)
+        #We read from cli if we expect less number of routes to avoid cli timeouts
+        if hosts <= 10000:
+            assert_equal(len(connected_hosts), hosts)
+        ingress = hosts+1
+        for hostip, hostmac in hosts_config:
+                self.proxyarp_arpreply_verify(ingress,hostip,hostmac,PositiveTest = PositiveTest)
+                time.sleep(1)
+        self.cliExit()
+        return True
+
+class cluster_vrouter(object):
+    apps = ('org.onosproject.vrouter', 'org.onosproject.fwd')
+    device_id = 'of:' + get_mac()
+    vrouter_device_dict = { "devices" : {
+                "{}".format(device_id) : {
+                    "basic" : {
+                        "driver" : "softrouter"
+                    }
+                }
+             },
+          }
+    zebra_conf = '''
+password zebra
+log stdout
+service advanced-vty
+!
+!debug zebra rib
+!debug zebra kernel
+!debug zebra fpm
+!
+!interface eth1
+! ip address 10.10.0.3/16
+line vty
+ exec-timeout 0 0
+'''
+    test_path = os.path.dirname(os.path.realpath(__file__))
+    quagga_config_path = os.path.join(test_path, '..', 'setup/quagga-config')
+    onos_config_path = os.path.join(test_path, '..', 'setup/onos-config')
+    GATEWAY = '192.168.10.50'
+    INGRESS_PORT = 1
+    EGRESS_PORT = 2
+    MAX_PORTS = 100
+    peer_list = [ ('192.168.10.1', '00:00:00:00:00:01'), ('192.168.11.1', '00:00:00:00:02:01'), ]
+    network_list = []
+    network_mask = 24
+    default_routes_address = ('11.10.10.0/24',)
+    default_peer_address = peer_list
+    quagga_ip = os.getenv('QUAGGA_IP')
+
+    @classmethod
+    def setUpClass(cls):
+        ''' Activate the vrouter apps'''
+        cls.olt = OltConfig()
+        cls.port_map, _ = cls.olt.olt_port_map()
+        if not cls.port_map:
+            cls.port_map = g_subscriber_port_map
+        time.sleep(3)
+        cls.load_device_id()
+
+    @classmethod
+    def tearDownClass(cls):
+        '''Deactivate the vrouter apps'''
+        #cls.vrouter_host_unload()
+        cls.start_onos(network_cfg = {})
+	#cls.vrouter_activate(cls, deactivate = True)
+
+
+    @classmethod
+    def load_device_id(cls):
+        did = OnosCtrl.get_device_id()
+        cls.device_id = did
+        cls.vrouter_device_dict = { "devices" : {
+                "{}".format(did) : {
+                    "basic" : {
+                        "driver" : "softrouter"
+                    }
+                }
+            },
+        }
+
+    def cliEnter(self):
+        retries = 0
+        while retries < 3:
+            self.cli = OnosCliDriver(connect = True)
+            if self.cli.handle:
+                break
+            else:
+                retries += 1
+                time.sleep(2)
+
+    def cliExit(self):
+        self.cli.disconnect()
+
+    @classmethod
+    def onos_load_config(cls, config):
+        status, code = OnosCtrl.config(config)
+        if status is False:
+            log.info('JSON request returned status %d' %code)
+            assert_equal(status, True)
+
+    @classmethod
+    def vrouter_config_get(cls, networks = 4, peers = 1, peer_address = None,
+                           route_update = None, router_address = None):
+        vrouter_configs = cls.generate_vrouter_conf(networks = networks, peers = peers,
+                                                    peer_address = peer_address, router_address = router_address)
+        return vrouter_configs
+
+    @classmethod
+    def vrouter_host_load(cls, peer_address = None):
+	#cls.setUpClass()
+        index = 1
+        peer_info = peer_address if peer_address is not None else cls.peer_list
+
+        for host,_ in peer_info:
+            iface = cls.port_map[index]
+            index += 1
+            log.info('Assigning ip %s to interface %s' %(host, iface))
+            config_cmds = ( 'ifconfig {} 0'.format(iface),
+                            'ifconfig {0} {1}'.format(iface, host),
+                            'arping -I {0} {1} -c 2'.format(iface, host),
+                            )
+            for cmd in config_cmds:
+                os.system(cmd)
+
+    @classmethod
+    def vrouter_host_unload(cls, peer_address = None):
+        index = 1
+        peer_info = peer_address if peer_address is not None else cls.peer_list
+
+        for host,_ in peer_info:
+            iface = cls.port_map[index]
+            index += 1
+            config_cmds = ('ifconfig {} 0'.format(iface), )
+            for cmd in config_cmds:
+                os.system(cmd)
+
+    @classmethod
+    def start_onos(cls, network_cfg = None):
+        if type(network_cfg) is tuple:
+            res = []
+            for v in network_cfg:
+                res += v.items()
+            config = dict(res)
+        else:
+            config = network_cfg
+        log.info('Restarting ONOS with new network configuration %s'%config)
+        #return CordTestServer().restart_onos(config)
+        return cord_test_onos_restart(config = config)
+
+    @classmethod
+    def start_quagga(cls, networks = 4, peer_address = None, router_address = None):
+        log.info('Restarting Quagga container with configuration for %d networks' %(networks))
+        config = cls.generate_conf(networks = networks, peer_address = peer_address, router_address = router_address)
+        if networks <= 10000:
+            boot_delay = 25
+        else:
+            delay_map = [60, 100, 150, 200, 300, 450, 600, 800, 1000, 1200]
+            n = min(networks/100000, len(delay_map)-1)
+            boot_delay = delay_map[n]
+        cord_test_quagga_restart(config = config, boot_delay = boot_delay)
+
+    @classmethod
+    def generate_vrouter_conf(cls, networks = 4, peers = 1, peer_address = None, router_address = None):
+        num = 0
+        if peer_address is None:
+           start_peer = ( 192 << 24) | ( 168 << 16)  |  (10 << 8) | 0
+           end_peer =   ( 200 << 24 ) | (168 << 16)  |  (10 << 8) | 0
+        else:
+           ip = peer_address[0][0]
+           start_ip = ip.split('.')
+           start_peer = ( int(start_ip[0]) << 24) | ( int(start_ip[1]) << 16)  |  ( int(start_ip[2]) << 8) | 0
+           end_peer =   ((int(start_ip[0]) + 8) << 24 ) | (int(start_ip[1]) << 16)  |  (int(start_ip[2]) << 8) | 0
+        local_network = end_peer + 1
+        ports_dict = { 'ports' : {} }
+        interface_list = []
+        peer_list = []
+        for n in xrange(start_peer, end_peer, 256):
+            port_map = ports_dict['ports']
+            port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
+            device_port_key = '{0}/{1}'.format(cls.device_id, port)
+            try:
+                interfaces = port_map[device_port_key]['interfaces']
+            except:
+                port_map[device_port_key] = { 'interfaces' : [] }
+                interfaces = port_map[device_port_key]['interfaces']
+            ip = n + 2
+            peer_ip = n + 1
+            ips = '%d.%d.%d.%d/24'%( (ip >> 24) & 0xff, ( (ip >> 16) & 0xff ), ( (ip >> 8 ) & 0xff ), ip & 0xff)
+            peer = '%d.%d.%d.%d' % ( (peer_ip >> 24) & 0xff, ( ( peer_ip >> 16) & 0xff ), ( (peer_ip >> 8 ) & 0xff ), peer_ip & 0xff )
+            mac = RandMAC()._fix()
+            peer_list.append((peer, mac))
+            if num < cls.MAX_PORTS - 1:
+                interface_dict = { 'name' : 'b1-{}'.format(port), 'ips': [ips], 'mac' : mac }
+                interfaces.append(interface_dict)
+                interface_list.append(interface_dict['name'])
+            else:
+                interfaces[0]['ips'].append(ips)
+            num += 1
+            if num == peers:
+                break
+        quagga_dict = { 'apps': { 'org.onosproject.router' : { 'router' : {}, 'bgp' : { 'bgpSpeakers' : [] } } } }
+        quagga_router_dict = quagga_dict['apps']['org.onosproject.router']['router']
+        quagga_router_dict['ospfEnabled'] = True
+        quagga_router_dict['interfaces'] = interface_list
+        quagga_router_dict['controlPlaneConnectPoint'] = '{0}/{1}'.format(cls.device_id, peers + 1)
+
+        #bgp_speaker_dict = { 'apps': { 'org.onosproject.router' : { 'bgp' : { 'bgpSpeakers' : [] } } } }
+        bgp_speakers_list = quagga_dict['apps']['org.onosproject.router']['bgp']['bgpSpeakers']
+        speaker_dict = {}
+        speaker_dict['name'] = 'bgp{}'.format(peers+1)
+        speaker_dict['connectPoint'] = '{0}/{1}'.format(cls.device_id, peers + 1)
+        speaker_dict['peers'] = peer_list
+        bgp_speakers_list.append(speaker_dict)
+        cls.peer_list = peer_list
+        return (cls.vrouter_device_dict, ports_dict, quagga_dict)
+
+    @classmethod
+    def generate_conf(cls, networks = 4, peer_address = None, router_address = None):
+        num = 0
+        if router_address is None:
+            start_network = ( 11 << 24) | ( 10 << 16) | ( 10 << 8) | 0
+            end_network =   ( 172 << 24 ) | ( 0 << 16)  | (0 << 8) | 0
+            network_mask = 24
+        else:
+           ip = router_address
+           start_ip = ip.split('.')
+           network_mask = int(start_ip[3].split('/')[1])
+           start_ip[3] = (start_ip[3].split('/'))[0]
+           start_network = (int(start_ip[0]) << 24) | ( int(start_ip[1]) << 16)  |  ( int(start_ip[2]) << 8) | 0
+           end_network = (172 << 24 ) | (int(start_ip[1]) << 16)  |  (int(start_ip[2]) << 8) | 0
+        net_list = []
+        peer_list = peer_address if peer_address is not None else cls.peer_list
+        network_list = []
+        for n in xrange(start_network, end_network, 256):
+            net = '%d.%d.%d.0'%( (n >> 24) & 0xff, ( ( n >> 16) & 0xff ), ( (n >> 8 ) & 0xff ) )
+            network_list.append(net)
+            gateway = peer_list[num % len(peer_list)][0]
+            net_route = 'ip route {0}/{1} {2}'.format(net, network_mask, gateway)
+            net_list.append(net_route)
+            num += 1
+            if num == networks:
+                break
+        cls.network_list = network_list
+        cls.network_mask = network_mask
+        zebra_routes = '\n'.join(net_list)
+        #log.info('Zebra routes: \n:%s\n' %cls.zebra_conf + zebra_routes)
+        return cls.zebra_conf + zebra_routes
+
+    @classmethod
+    def vrouter_activate(cls, deactivate = False):
+        app = 'org.onosproject.vrouter'
+        onos_ctrl = OnosCtrl(app)
+        if deactivate is True:
+            onos_ctrl.deactivate()
+        else:
+            onos_ctrl.activate()
+        time.sleep(3)
+
+    @classmethod
+    def vrouter_configure(cls, networks = 4, peers = 1, peer_address = None,
+                          route_update = None, router_address = None, time_expire = None, adding_new_routes = None):
+        vrouter_configs = cls.vrouter_config_get(networks = networks, peers = peers,
+                                                 peer_address = peer_address, route_update = route_update)
+        cls.start_onos(network_cfg = vrouter_configs)
+        cls.vrouter_host_load()
+        ##Start quagga
+        cls.start_quagga(networks = networks, peer_address = peer_address, router_address = router_address)
+        return vrouter_configs
+
+    def vrouter_port_send_recv(self, ingress, egress, dst_mac, dst_ip, positive_test = True):
+        src_mac = '00:00:00:00:00:02'
+        src_ip = '1.1.1.1'
+        self.success = False if positive_test else True
+        timeout = 10 if positive_test else 1
+        count = 2 if positive_test else 1
+        self.start_sending = True
+        def recv_task():
+            def recv_cb(pkt):
+                log.info('Pkt seen with ingress ip %s, egress ip %s' %(pkt[IP].src, pkt[IP].dst))
+                self.success = True if positive_test else False
+            sniff(count=count, timeout=timeout,
+                  lfilter = lambda p: IP in p and p[IP].dst == dst_ip and p[IP].src == src_ip,
+                  prn = recv_cb, iface = self.port_map[ingress])
+            self.start_sending = False
+
+        t = threading.Thread(target = recv_task)
+        t.start()
+        L2 = Ether(src = src_mac, dst = dst_mac)
+        L3 = IP(src = src_ip, dst = dst_ip)
+        pkt = L2/L3
+        log.info('Sending a packet with dst ip %s, dst mac %s on port %s to verify if flows are correct' %
+                 (dst_ip, dst_mac, self.port_map[egress]))
+        while self.start_sending is True:
+            sendp(pkt, count=50, iface = self.port_map[egress])
+        t.join()
+        assert_equal(self.success, True)
+
+    def vrouter_traffic_verify(self, positive_test = True, peer_address = None):
+        if peer_address is None:
+            peers = len(self.peer_list)
+            peer_list = self.peer_list
+        else:
+            peers = len(peer_address)
+            peer_list = peer_address
+        egress = peers + 1
+        num = 0
+        num_hosts = 5 if positive_test else 1
+        src_mac = '00:00:00:00:00:02'
+        src_ip = '1.1.1.1'
+        if self.network_mask != 24:
+            peers = 1
+        for network in self.network_list:
+            num_ips = num_hosts
+            octets = network.split('.')
+            for i in xrange(num_ips):
+                octets[-1] = str(int(octets[-1]) + 1)
+                dst_ip = '.'.join(octets)
+                dst_mac = peer_list[ num % peers ] [1]
+                port = (num % peers)
+                ingress = port + 1
+                #Since peers are on the same network
+                ##Verify if flows are setup by sending traffic across
+                self.vrouter_port_send_recv(ingress, egress, dst_mac, dst_ip, positive_test = positive_test)
+            num += 1
+
+    def vrouter_network_verify(self, networks, peers = 1, positive_test = True,
+                                 start_network = None, start_peer_address = None, route_update = None,
+                                 invalid_peers = None, time_expire = None, unreachable_route_traffic = None,
+                                 deactivate_activate_vrouter = None, adding_new_routes = None):
+
+        _, ports_map, egress_map = self.vrouter_configure(networks = networks, peers = peers,
+                                                          peer_address = start_peer_address,
+                                                          route_update = route_update,
+                                                          router_address = start_network,
+                                                          time_expire = time_expire,
+                                                          adding_new_routes = adding_new_routes)
+        self.cliEnter()
+        ##Now verify
+        hosts = json.loads(self.cli.hosts(jsonFormat = True))
+        log.info('Discovered hosts: %s' %hosts)
+        ##We read from cli if we expect less number of routes to avoid cli timeouts
+        if networks <= 10000:
+            routes = json.loads(self.cli.routes(jsonFormat = True))
+            #log.info('Routes: %s' %routes)
+            if start_network is not None:
+               if start_network.split('/')[1] is 24:
+                  assert_equal(len(routes['routes4']), networks)
+               if start_network.split('/')[1] is not 24:
+                  assert_equal(len(routes['routes4']), 1)
+            if start_network is None and invalid_peers is None:
+               assert_equal(len(routes['routes4']), networks)
+            if invalid_peers is not None:
+               assert_equal(len(routes['routes4']), 0)
+            flows = json.loads(self.cli.flows(jsonFormat = True))
+            flows = filter(lambda f: f['flows'], flows)
+            #log.info('Flows: %s' %flows)
+            assert_not_equal(len(flows), 0)
+        if invalid_peers is None:
+            self.vrouter_traffic_verify()
+        if positive_test is False:
+            self.vrouter_network_verify_negative(networks, peers = peers)
+        if time_expire is True:
+            self.start_quagga(networks = networks, peer_address = start_peer_address, router_address = '12.10.10.1/24')
+            self.vrouter_traffic_verify()
+        if unreachable_route_traffic is True:
+            network_list_backup = self.network_list
+            self.network_list = ['2.2.2.2','3.3.3.3','4.4.4.4','5.5.5.5']
+            self.vrouter_traffic_verify(positive_test = False)
+            self.network_list = network_list_backup
+        if deactivate_activate_vrouter is True:
+            log.info('Deactivating vrouter app in ONOS controller for negative scenario')
+            self.vrouter_activate(deactivate = True)
+            #routes = json.loads(self.cli.routes(jsonFormat = False, cmd_exist = False))
+            #assert_equal(len(routes['routes4']), 'Command not found')
+            log.info('Activating vrouter app again in ONOS controller for negative scenario')
+            self.vrouter_activate(deactivate = False)
+            routes = json.loads(self.cli.routes(jsonFormat = True))
+            assert_equal(len(routes['routes4']), networks)
+            self.vrouter_traffic_verify()
+        self.cliExit()
+	return True
+
+    def vrouter_network_verify_negative(self, networks, peers = 1):
+        ##Stop quagga. Test traffic again to see if flows were removed
+        log.info('Stopping Quagga container')
+        cord_test_quagga_stop()
+        if networks <= 10000:
+            routes = json.loads(self.cli.routes(jsonFormat = True))
+            #Verify routes have been removed
+            if routes and routes.has_key('routes4'):
+                assert_equal(len(routes['routes4']), 0)
+        self.vrouter_traffic_verify(positive_test = False)
+        log.info('OVS flows have been removed successfully after Quagga was stopped')
+        self.start_quagga(networks = networks)
+        ##Verify the flows again after restarting quagga back
+        if networks <= 10000:
+            routes = json.loads(self.cli.routes(jsonFormat = True))
+            assert_equal(len(routes['routes4']), networks)
+        self.vrouter_traffic_verify()
+        log.info('OVS flows have been successfully reinstalled after Quagga was restarted')
+
+    def quagga_shell(self, cmd):
+        shell_cmds = ('vtysh', '"conf t"', '"{}"'.format(cmd))
+        quagga_cmd = ' -c '.join(shell_cmds)
+
+        return cord_test_quagga_shell(quagga_cmd)
+
+class cluster_acl(object):
+    app = ('org.onosproject.acl')
+    device_id = 'of:' + get_mac('ovsbr0')
+    test_path = os.path.dirname(os.path.realpath(__file__))
+    onos_config_path = os.path.join(test_path, '..', 'setup/onos-config')
+    GATEWAY = '192.168.10.50'
+    INGRESS_PORT = 1
+    EGRESS_PORT = 2
+    ingress_iface = 1
+    egress_iface = 2
+    MAX_PORTS = 100
+    CURRENT_PORT_NUM = egress_iface
+    ACL_SRC_IP = '192.168.20.3/32'
+    ACL_DST_IP = '192.168.30.2/32'
+    ACL_SRC_IP_RULE_2 = '192.168.40.3/32'
+    ACL_DST_IP_RULE_2 = '192.168.50.2/32'
+    ACL_SRC_IP_PREFIX_24 = '192.168.20.3/24'
+    ACL_DST_IP_PREFIX_24 = '192.168.30.2/24'
+    HOST_DST_IP = '192.168.30.0/24'
+    HOST_DST_IP_RULE_2 = '192.168.50.0/24'
+
+    @classmethod
+    def setUpClass(cls):
+        cls.olt = OltConfig()
+        cls.port_map,_ = cls.olt.olt_port_map()
+        if not cls.port_map:
+            cls.port_map = g_subscriber_port_map
+        time.sleep(3)
+        log.info('port_map = %s'%cls.port_map[1] )
+
+    @classmethod
+    def tearDownClass(cls):
+        '''Deactivate the acl app'''
+    def setUp(self):
+	self.setUpClass()
+        ''' Activate the acl app'''
+        self.maxDiff = None ##for assert_equal compare outputs on failure
+        self.onos_ctrl = OnosCtrl(self.app)
+        status, _ = self.onos_ctrl.activate()
+        assert_equal(status, True)
+        time.sleep(1)
+        #status, _ = ACLTest.remove_acl_rule()
+        #log.info('Start setup')
+        #assert_equal(status, True)
+
+    def tearDown(self):
+        '''Deactivate the acl app'''
+        log.info('Tear down setup')
+        self.CURRENT_PORT_NUM = 4
+
+    def cliEnter(self):
+        retries = 0
+        while retries < 3:
+            self.cli = OnosCliDriver(connect = True)
+            if self.cli.handle:
+                break
+            else:
+                retries += 1
+                time.sleep(2)
+
+    def cliExit(self):
+        self.cli.disconnect()
+
+    @classmethod
+    def acl_hosts_add(cls, dstHostIpMac, egress_iface_count = 1,  egress_iface_num = None):
+	cls.setUpClass()
+        index = 0
+        if egress_iface_num is None:
+            egress_iface_num = cls.egress_iface
+        for ip,_ in dstHostIpMac:
+            egress = cls.port_map[egress_iface_num]
+            log.info('Assigning ip %s to interface %s' %(ip, egress))
+            config_cmds_egress = ( 'ifconfig {} 0'.format(egress),
+                                   'ifconfig {0} up'.format(egress),
+                                   'ifconfig {0} {1}'.format(egress, ip),
+                                   'arping -I {0} {1} -c 2'.format(egress, ip.split('/')[0]),
+                                   'ifconfig {0}'.format(egress),
+                                 )
+            for cmd in config_cmds_egress:
+                os.system(cmd)
+            index += 1
+            if index == egress_iface_count:
+               break
+            egress_iface_count += 1
+            egress_iface_num += 1
+
+
+    @classmethod
+    def acl_hosts_remove(cls, egress_iface_count = 1,  egress_iface_num = None):
+	cls.setUpClass()
+        if egress_iface_num is None:
+           egress_iface_num = cls.egress_iface
+        n = 0
+        for n in range(egress_iface_count):
+           egress = cls.port_map[egress_iface_num]
+           config_cmds_egress = ('ifconfig {} 0'.format(egress))
+           os.system(config_cmds_egress)
+           egress_iface_num += 1
+
+    def acl_rule_traffic_send_recv(self, srcMac, dstMac, srcIp, dstIp, ingress =None, egress=None, ip_proto=None, dstPortNum = None, positive_test = True):
+	self.setUpClass()
+        if ingress is None:
+           ingress = self.ingress_iface
+        if egress is None:
+           egress = self.egress_iface
+        ingress = self.port_map[ingress]
+        egress = self.port_map[egress]
+        self.success = False if positive_test else True
+        timeout = 10 if positive_test else 1
+        count = 2 if positive_test else 1
+        self.start_sending = True
+        def recv_task():
+            def recv_cb(pkt):
+                log.info('Pkt seen with ingress ip %s, egress ip %s' %(pkt[IP].src, pkt[IP].dst))
+                self.success = True if positive_test else False
+            sniff(count=count, timeout=timeout,
+                  lfilter = lambda p: IP in p and p[IP].dst == dstIp.split('/')[0] and p[IP].src == srcIp.split('/')[0],
+                  prn = recv_cb, iface = egress)
+            self.start_sending = False
+
+        t = threading.Thread(target = recv_task)
+        t.start()
+        L2 = Ether(src = srcMac, dst = dstMac)
+        L3 = IP(src = srcIp.split('/')[0], dst = dstIp.split('/')[0])
+        pkt = L2/L3
+        log.info('Sending a packet with dst ip %s, src ip %s , dst mac %s src mac %s on port %s to verify if flows are correct' %
+                 (dstIp.split('/')[0], srcIp.split('/')[0], dstMac, srcMac, ingress))
+        while self.start_sending is True:
+            sendp(pkt, count=50, iface = ingress)
+        t.join()
+        assert_equal(self.success, True)
+
+    @classmethod
+    def onos_load_config(cls, config):
+        status, code = OnosCtrl.config(config)
+        if status is False:
+            log.info('JSON request returned status %d' %code)
+            assert_equal(status, True)
+
+class cluster_dhcprelay(object):
+    app = 'org.onosproject.dhcprelay'
+    app_dhcp = 'org.onosproject.dhcp'
+    relay_interfaces_last = ()
+    interface_to_mac_map = {}
+    host_ip_map = {}
+    test_path = os.path.dirname(os.path.realpath(__file__))
+    dhcp_data_dir = os.path.join(test_path, '..', 'setup')
+    olt_conf_file = os.path.join(test_path, '..', 'setup/olt_config.json')
+    default_config = { 'default-lease-time' : 600, 'max-lease-time' : 7200, }
+    default_options = [ ('subnet-mask', '255.255.255.0'),
+                     ('broadcast-address', '192.168.1.255'),
+                     ('domain-name-servers', '192.168.1.1'),
+                     ('domain-name', '"mydomain.cord-tester"'),
+                   ]
+    ##specify the IP for the dhcp interface matching the subnet and subnet config
+    ##this is done for each interface dhcpd server would be listening on
+    default_subnet_config = [ ('192.168.1.2',
+'''
+subnet 192.168.1.0 netmask 255.255.255.0 {
+    range 192.168.1.10 192.168.1.100;
+}
+'''), ]
+
+    lock = threading.Condition()
+    ip_count = 0
+    failure_count = 0
+    start_time = 0
+    diff = 0
+
+    transaction_count = 0
+    transactions = 0
+    running_time = 0
+    total_success = 0
+    total_failure = 0
+    onos_restartable = bool(int(os.getenv('ONOS_RESTART', 0)))
+
+    @classmethod
+    def setUpClass(cls):
+        ''' Activate the dhcprelay app'''
+        OnosCtrl(cls.app_dhcp).deactivate()
+        time.sleep(3)
+        cls.onos_ctrl = OnosCtrl(cls.app)
+        status, _ = cls.onos_ctrl.activate()
+        assert_equal(status, True)
+        time.sleep(3)
+        cls.dhcp_relay_setup()
+        ##start dhcpd initially with default config
+        cls.dhcpd_start()
+
+    @classmethod
+    def tearDownClass(cls):
+        '''Deactivate the dhcp relay app'''
+        try:
+            os.unlink('{}/dhcpd.conf'.format(cls.dhcp_data_dir))
+            os.unlink('{}/dhcpd.leases'.format(cls.dhcp_data_dir))
+        except: pass
+        cls.onos_ctrl.deactivate()
+        cls.dhcpd_stop()
+        cls.dhcp_relay_cleanup()
+
+    @classmethod
+    def dhcp_relay_setup(cls):
+        did = OnosCtrl.get_device_id()
+        cls.relay_device_id = did
+        cls.olt = OltConfig(olt_conf_file = cls.olt_conf_file)
+        cls.port_map, _ = cls.olt.olt_port_map()
+        if cls.port_map:
+            ##Per subscriber, we use 1 relay port
+            try:
+                relay_port = cls.port_map[cls.port_map['relay_ports'][0]]
+            except:
+                relay_port = cls.port_map['uplink']
+            cls.relay_interface_port = relay_port
+            cls.relay_interfaces = (cls.port_map[cls.relay_interface_port],)
+        else:
+            cls.relay_interface_port = 100
+            cls.relay_interfaces = (g_subscriber_port_map[cls.relay_interface_port],)
+        cls.relay_interfaces_last = cls.relay_interfaces
+        if cls.port_map:
+            ##generate a ip/mac client virtual interface config for onos
+            interface_list = []
+            for port in cls.port_map['ports']:
+                port_num = cls.port_map[port]
+                if port_num == cls.port_map['uplink']:
+                    continue
+                ip = cls.get_host_ip(port_num)
+                mac = cls.get_mac(port)
+                interface_list.append((port_num, ip, mac))
+
+            #configure dhcp server virtual interface on the same subnet as first client interface
+            relay_ip = cls.get_host_ip(interface_list[0][0])
+            relay_mac = cls.get_mac(cls.port_map[cls.relay_interface_port])
+            interface_list.append((cls.relay_interface_port, relay_ip, relay_mac))
+            cls.onos_interface_load(interface_list)
+
+    @classmethod
+    def dhcp_relay_cleanup(cls):
+        ##reset the ONOS port configuration back to default
+        if cls.onos_restartable is True:
+            log.info('Cleaning up dhcp relay config by restarting ONOS with default network cfg')
+            return cord_test_onos_restart(config = {})
+
+    @classmethod
+    def onos_load_config(cls, config):
+        status, code = OnosCtrl.config(config)
+        if status is False:
+            log.info('JSON request returned status %d' %code)
+            assert_equal(status, True)
+        time.sleep(2)
+
+    @classmethod
+    def onos_interface_load(cls, interface_list):
+        interface_dict = { 'ports': {} }
+        for port_num, ip, mac in interface_list:
+            port_map = interface_dict['ports']
+            port = '{}/{}'.format(cls.relay_device_id, port_num)
+            port_map[port] = { 'interfaces': [] }
+            interface_list = port_map[port]['interfaces']
+            interface_map = { 'ips' : [ '{}/{}'.format(ip, 24) ],
+                              'mac' : mac,
+                              'name': 'vir-{}'.format(port_num)
+                            }
+            interface_list.append(interface_map)
+
+        cls.onos_load_config(interface_dict)
+
+    @classmethod
+    def onos_dhcp_relay_load(cls, server_ip, server_mac):
+        relay_device_map = '{}/{}'.format(cls.relay_device_id, cls.relay_interface_port)
+        dhcp_dict = {'apps':{'org.onosproject.dhcp-relay':{'dhcprelay':
+                                                          {'dhcpserverConnectPoint':relay_device_map,
+                                                           'serverip':server_ip,
+                                                           'servermac':server_mac
+                                                           }
+                                                           }
+                             }
+                     }
+        cls.onos_load_config(dhcp_dict)
+
+    @classmethod
+    def get_host_ip(cls, port):
+        if cls.host_ip_map.has_key(port):
+            return cls.host_ip_map[port]
+        cls.host_ip_map[port] = '192.168.1.{}'.format(port)
+        return cls.host_ip_map[port]
+
+    @classmethod
+    def host_load(cls, iface):
+        '''Have ONOS discover the hosts for dhcp-relay responses'''
+        port = g_subscriber_port_map[iface]
+        host = '173.17.1.{}'.format(port)
+        cmds = ( 'ifconfig {} 0'.format(iface),
+                 'ifconfig {0} {1}'.format(iface, host),
+                 'arping -I {0} {1} -c 2'.format(iface, host),
+                 'ifconfig {} 0'.format(iface), )
+        for c in cmds:
+            os.system(c)
+    @classmethod
+
+    def dhcpd_conf_generate(cls, config = default_config, options = default_options,
+                            subnet = default_subnet_config):
+        conf = ''
+        for k, v in config.items():
+            conf += '{} {};\n'.format(k, v)
+
+        opts = ''
+        for k, v in options:
+            opts += 'option {} {};\n'.format(k, v)
+
+        subnet_config = ''
+        for _, v in subnet:
+            subnet_config += '{}\n'.format(v)
+
+        return '{}{}{}'.format(conf, opts, subnet_config)
+
+    @classmethod
+    def dhcpd_start(cls, intf_list = None,
+                    config = default_config, options = default_options,
+                    subnet = default_subnet_config):
+        '''Start the dhcpd server by generating the conf file'''
+        if intf_list is None:
+            intf_list = cls.relay_interfaces
+        ##stop dhcpd if already running
+        cls.dhcpd_stop()
+        dhcp_conf = cls.dhcpd_conf_generate(config = config, options = options,
+                                            subnet = subnet)
+        ##first touch dhcpd.leases if it doesn't exist
+        lease_file = '{}/dhcpd.leases'.format(cls.dhcp_data_dir)
+        if os.access(lease_file, os.F_OK) is False:
+            with open(lease_file, 'w') as fd: pass
+
+        conf_file = '{}/dhcpd.conf'.format(cls.dhcp_data_dir)
+        with open(conf_file, 'w') as fd:
+            fd.write(dhcp_conf)
+
+        #now configure the dhcpd interfaces for various subnets
+        index = 0
+        intf_info = []
+        for ip,_ in subnet:
+            intf = intf_list[index]
+            mac = cls.get_mac(intf)
+            intf_info.append((ip, mac))
+            index += 1
+            os.system('ifconfig {} {}'.format(intf, ip))
+
+        intf_str = ','.join(intf_list)
+        dhcpd_cmd = '/usr/sbin/dhcpd -4 --no-pid -cf {0} -lf {1} {2}'.format(conf_file, lease_file, intf_str)
+        log.info('Starting DHCPD server with command: %s' %dhcpd_cmd)
+        ret = os.system(dhcpd_cmd)
+        assert_equal(ret, 0)
+        time.sleep(3)
+        cls.relay_interfaces_last = cls.relay_interfaces
+        cls.relay_interfaces = intf_list
+        cls.onos_dhcp_relay_load(*intf_info[0])
+
+    @classmethod
+    def dhcpd_stop(cls):
+        os.system('pkill -9 dhcpd')
+        for intf in cls.relay_interfaces:
+            os.system('ifconfig {} 0'.format(intf))
+
+        cls.relay_interfaces = cls.relay_interfaces_last
+
+    @classmethod
+    def get_mac(cls, iface):
+        if cls.interface_to_mac_map.has_key(iface):
+            return cls.interface_to_mac_map[iface]
+        mac = get_mac(iface, pad = 0)
+        cls.interface_to_mac_map[iface] = mac
+        return mac
+
+    def stats(self,success_rate = False, only_discover = False, iface = 'veth0'):
+
+        self.ip_count = 0
+        self.failure_count = 0
+        self.start_time = 0
+        self.diff = 0
+        self.transaction_count = 0
+
+        mac = self.get_mac(iface)
+        self.host_load(iface)
+        ##we use the defaults for this test that serves as an example for others
+        ##You don't need to restart dhcpd server if retaining default config
+        config = self.default_config
+        options = self.default_options
+        subnet = self.default_subnet_config
+        dhcpd_interface_list = self.relay_interfaces
+        self.dhcpd_start(intf_list = dhcpd_interface_list,
+                         config = config,
+                         options = options,
+                         subnet = subnet)
+        self.dhcp = DHCPTest(seed_ip = '182.17.0.1', iface = iface)
+        self.start_time = time.time()
+
+        while self.diff <= 60:
+
+            if only_discover:
+                cip, sip, mac, _ = self.dhcp.only_discover(multiple = True)
+                log.info('Got dhcp client IP %s from server %s for mac %s' %
+                        (cip, sip, mac))
+            else:
+                cip, sip = self.send_recv(mac, update_seed = True, validate = False)
+
+            if cip:
+                self.ip_count +=1
+            elif cip == None:
+                self.failure_count += 1
+                log.info('Failed to get ip')
+                if success_rate and self.ip_count > 0:
+                        break
+
+            self.diff = round(time.time() - self.start_time, 0)
+
+        self.transaction_count = round((self.ip_count+self.failure_count)/self.diff, 2)
+        self.transactions += (self.ip_count+self.failure_count)
+        self.running_time += self.diff
+        self.total_success += self.ip_count
+        self.total_failure += self.failure_count
+
+    def send_recv(self, mac, update_seed = False, validate = True):
+        cip, sip = self.dhcp.discover(mac = mac, update_seed = update_seed)
+        if validate:
+            assert_not_equal(cip, None)
+            assert_not_equal(sip, None)
+        log.info('Got dhcp client IP %s from server %s for mac %s' %
+                (cip, sip, self.dhcp.get_mac(cip)[0]))
+        return cip,sip
+
+class Subscriber(Channels):
+    PORT_TX_DEFAULT = 2
+    PORT_RX_DEFAULT = 1
+    INTF_TX_DEFAULT = 'veth2'
+    INTF_RX_DEFAULT = 'veth0'
+    STATS_RX = 0
+    STATS_TX = 1
+    STATS_JOIN = 2
+    STATS_LEAVE = 3
+    SUBSCRIBER_SERVICES = 'DHCP IGMP TLS'
+    def __init__(self, name = 'sub', service = SUBSCRIBER_SERVICES, port_map = None,
+        num = 1, channel_start = 0,
+        tx_port = PORT_TX_DEFAULT, rx_port = PORT_RX_DEFAULT,
+        iface = INTF_RX_DEFAULT, iface_mcast = INTF_TX_DEFAULT,
+        mcast_cb = None, loginType = 'wireless'):
+        self.tx_port = tx_port
+        self.rx_port = rx_port
+        self.port_map = port_map or g_subscriber_port_map
+        try:
+            self.tx_intf = self.port_map[tx_port]
+            self.rx_intf = self.port_map[rx_port]
+        except:
+            self.tx_intf = self.port_map[self.PORT_TX_DEFAULT]
+            self.rx_intf = self.port_map[self.PORT_RX_DEFAULT]
+        log.info('Subscriber %s, rx interface %s, uplink interface %s' %(name, self.rx_intf, self.tx_intf))
+        Channels.__init__(self, num, channel_start = channel_start,
+                                   iface = self.rx_intf, iface_mcast = self.tx_intf, mcast_cb = mcast_cb)
+        self.name = name
+        self.service = service
+        self.service_map = {}
+        services = self.service.strip().split(' ')
+        for s in services:
+            self.service_map[s] = True
+        self.loginType = loginType
+        ##start streaming channels
+        self.join_map = {}
+        ##accumulated join recv stats
+        self.join_rx_stats = Stats()
+        self.recv_timeout = False
+
+    def has_service(self, service):
+        if self.service_map.has_key(service):
+            return self.service_map[service]
+        if self.service_map.has_key(service.upper()):
+            return self.service_map[service.upper()]
+        return False
+
+    def channel_join_update(self, chan, join_time):
+        self.join_map[chan] = ( Stats(), Stats(), Stats(), Stats() )
+        self.channel_update(chan, self.STATS_JOIN, 1, t = join_time)
+    def channel_join(self, chan = 0, delay = 2):
+        '''Join a channel and create a send/recv stats map'''
+        if self.join_map.has_key(chan):
+            del self.join_map[chan]
+        self.delay = delay
+        chan, join_time = self.join(chan)
+        self.channel_join_update(chan, join_time)
+        return chan
+
+    def channel_join_next(self, delay = 2):
+        '''Joins the next channel leaving the last channel'''
+        if self.last_chan:
+            if self.join_map.has_key(self.last_chan):
+                del self.join_map[self.last_chan]
+        self.delay = delay
+        chan, join_time = self.join_next()
+        self.channel_join_update(chan, join_time)
+        return chan
+
+    def channel_jump(self, delay = 2):
+        '''Jumps randomly to the next channel leaving the last channel'''
+        if self.last_chan is not None:
+            if self.join_map.has_key(self.last_chan):
+                del self.join_map[self.last_chan]
+        self.delay = delay
+        chan, join_time = self.jump()
+        self.channel_join_update(chan, join_time)
+        return chan
+
+    def channel_leave(self, chan = 0):
+        if self.join_map.has_key(chan):
+            del self.join_map[chan]
+        self.leave(chan)
+
+    def channel_update(self, chan, stats_type, packets, t=0):
+        if type(chan) == type(0):
+            chan_list = (chan,)
+        else:
+            chan_list = chan
+        for c in chan_list:
+            if self.join_map.has_key(c):
+                self.join_map[c][stats_type].update(packets = packets, t = t)
+    def channel_receive(self, chan, cb = None, count = 1, timeout = 5):
+        log.info('Subscriber %s on port %s receiving from group %s, channel %d' %
+                  (self.name, self.rx_intf, self.gaddr(chan), chan))
+        r = self.recv(chan, cb = cb, count = count, timeout = timeout)
+        if len(r) == 0:
+            log.info('Subscriber %s on port %s timed out' %(self.name, self.rx_intf))
+        else:
+            log.info('Subscriber %s on port %s received %d packets' %(self.name, self.rx_intf, len(r)))
+        if self.recv_timeout:
+            ##Negative test case is disabled for now
+            assert_equal(len(r), 0)
+
+    def recv_channel_cb(self, pkt):
+        ##First verify that we have received the packet for the joined instance
+        log.info('Packet received for group %s, subscriber %s, port %s' %
+                 (pkt[IP].dst, self.name, self.rx_intf))
+        if self.recv_timeout:
+            return
+        chan = self.caddr(pkt[IP].dst)
+        assert_equal(chan in self.join_map.keys(), True)
+        recv_time = monotonic.monotonic() * 1000000
+        join_time = self.join_map[chan][self.STATS_JOIN].start
+        delta = recv_time - join_time
+        self.join_rx_stats.update(packets=1, t = delta, usecs = True)
+        self.channel_update(chan, self.STATS_RX, 1, t = delta)
+        log.debug('Packet received in %.3f usecs for group %s after join' %(delta, pkt[IP].dst))
+
+class subscriber_pool:
+
+    def __init__(self, subscriber, test_cbs):
+        self.subscriber = subscriber
+        self.test_cbs = test_cbs
+
+    def pool_cb(self):
+        for cb in self.test_cbs:
+            if cb:
+                self.test_status = cb(self.subscriber)
+                if self.test_status is not True:
+                    ## This is chaning for other sub status has to check again
+                    self.test_status = True
+                    log.info('This service is failed and other services will not run for this subscriber')
+                    break
+        log.info('This Subscriber is tested for multiple service eligibility ')
+        self.test_status = True
+
+class cluster_subscriber(object):
+      apps = ('org.opencord.aaa', 'org.onosproject.dhcp')
+      olt_apps = () #'org.opencord.cordmcast')
+      vtn_app = 'org.opencord.vtn'
+      table_app = 'org.ciena.cordigmp'
+      dhcp_server_config = {
+        "ip": "10.1.11.50",
+        "mac": "ca:fe:ca:fe:ca:fe",
+        "subnet": "255.255.252.0",
+        "broadcast": "10.1.11.255",
+        "router": "10.1.8.1",
+        "domain": "8.8.8.8",
+        "ttl": "63",
+        "delay": "2",
+        "startip": "10.1.11.51",
+        "endip": "10.1.11.100"
+      }
+
+      aaa_loaded = False
+      test_path = os.path.dirname(os.path.realpath(__file__))
+      table_app_file = os.path.join(test_path, '..', 'apps/ciena-cordigmp-multitable-2.0-SNAPSHOT.oar')
+      app_file = os.path.join(test_path, '..', 'apps/ciena-cordigmp-2.0-SNAPSHOT.oar')
+      onos_config_path = os.path.join(test_path, '..', 'setup/onos-config')
+      olt_conf_file = os.path.join(test_path, '..', 'setup/olt_config.json')
+      cpqd_path = os.path.join(test_path, '..', 'setup')
+      ovs_path = cpqd_path
+      test_services = ('IGMP', 'TRAFFIC')
+      num_joins = 0
+      num_subscribers = 0
+      num_channels = 0
+      recv_timeout = False
+      onos_restartable = bool(int(os.getenv('ONOS_RESTART', 0)))
+
+      INTF_TX_DEFAULT = 'veth2'
+      INTF_RX_DEFAULT = 'veth0'
+      SUBSCRIBER_TIMEOUT = 300
+      CLIENT_CERT = """-----BEGIN CERTIFICATE-----
+MIICuDCCAiGgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBizELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTb21ld2hlcmUxEzARBgNVBAoTCkNpZW5h
+IEluYy4xHjAcBgkqhkiG9w0BCQEWD2FkbWluQGNpZW5hLmNvbTEmMCQGA1UEAxMd
+RXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTYwNjA2MjExMjI3WhcN
+MTcwNjAxMjExMjI3WjBnMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNV
+BAoTCkNpZW5hIEluYy4xFzAVBgNVBAMUDnVzZXJAY2llbmEuY29tMR0wGwYJKoZI
+hvcNAQkBFg51c2VyQGNpZW5hLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
+gYEAwvXiSzb9LZ6c7uNziUfKvoHO7wu/uiFC5YUpXbmVGuGZizbVrny0xnR85Dfe
++9R4diansfDhIhzOUl1XjN3YDeSS9OeF5YWNNE8XDhlz2d3rVzaN6hIhdotBkUjg
+rUewjTg5OFR31QEyG3v8xR3CLgiE9xQELjZbSA07pD79zuUCAwEAAaNPME0wEwYD
+VR0lBAwwCgYIKwYBBQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL3d3dy5l
+eGFtcGxlLmNvbS9leGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOBgQDAjkrY
+6tDChmKbvr8w6Du/t8vHjTCoCIocHTN0qzWOeb1YsAGX89+TrWIuO1dFyYd+Z0KC
+PDKB5j/ygml9Na+AklSYAVJIjvlzXKZrOaPmhZqDufi+rXWti/utVqY4VMW2+HKC
+nXp37qWeuFLGyR1519Y1d6F/5XzqmvbwURuEug==
+-----END CERTIFICATE-----"""
+
+      CLIENT_CERT_INVALID = '''-----BEGIN CERTIFICATE-----
+MIIDvTCCAqWgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBizELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlTb21ld2hlcmUxEzARBgNVBAoTCkNpZW5h
+IEluYy4xHjAcBgkqhkiG9w0BCQEWD2FkbWluQGNpZW5hLmNvbTEmMCQGA1UEAxMd
+RXhhbXBsZSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTYwMzExMTg1MzM2WhcN
+MTcwMzA2MTg1MzM2WjBnMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEzARBgNV
+BAoTCkNpZW5hIEluYy4xFzAVBgNVBAMUDnVzZXJAY2llbmEuY29tMR0wGwYJKoZI
+hvcNAQkBFg51c2VyQGNpZW5hLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
+AQoCggEBAOxemcBsPn9tZsCa5o2JA6sQDC7A6JgCNXXl2VFzKLNNvB9PS6D7ZBsQ
+5An0zEDMNzi51q7lnrYg1XyiE4S8FzMGAFr94RlGMQJUbRD9V/oqszMX4k++iAOK
+tIA1gr3x7Zi+0tkjVSVzXTmgNnhChAamdMsjYUG5+CY9WAicXyy+VEV3zTphZZDR
+OjcjEp4m/TSXVPYPgYDXI40YZKX5BdvqykWtT/tIgZb48RS1NPyN/XkCYzl3bv21
+qx7Mc0fcEbsJBIIRYTUkfxnsilcnmLxSYO+p+DZ9uBLBzcQt+4Rd5pLSfi21WM39
+2Z2oOi3vs/OYAPAqgmi2JWOv3mePa/8CAwEAAaNPME0wEwYDVR0lBAwwCgYIKwYB
+BQUHAwIwNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL3d3dy5leGFtcGxlLmNvbS9l
+eGFtcGxlX2NhLmNybDANBgkqhkiG9w0BAQUFAAOCAQEALBzMPDTIB6sLyPl0T6JV
+MjOkyldAVhXWiQsTjaGQGJUUe1cmUJyZbUZEc13MygXMPOM4x7z6VpXGuq1c/Vxn
+VzQ2fNnbJcIAHi/7G8W5/SQfPesIVDsHTEc4ZspPi5jlS/MVX3HOC+BDbOjdbwqP
+RX0JEr+uOyhjO+lRxG8ilMRACoBUbw1eDuVDoEBgErSUC44pq5ioDw2xelc+Y6hQ
+dmtYwfY0DbvwxHtA495frLyPcastDiT/zre7NL51MyUDPjjYjghNQEwvu66IKbQ3
+T1tJBrgI7/WI+dqhKBFolKGKTDWIHsZXQvZ1snGu/FRYzg1l+R/jT8cRB9BDwhUt
+yg==
+-----END CERTIFICATE-----'''
+      @classmethod
+      def load_device_id(cls):
+            '''Configure the device id'''
+            did = OnosCtrl.get_device_id()
+            #Set the default config
+            cls.device_id = did
+            cls.device_dict = { "devices" : {
+                        "{}".format(did) : {
+                              "basic" : {
+                                    "driver" : "pmc-olt"
+                                    }
+                              }
+                        },
+                  }
+            return did
+
+      @classmethod
+      def setUpClass(cls):
+          '''Load the OLT config and activate relevant apps'''
+          did = cls.load_device_id()
+          network_cfg = { "devices" : {
+                  "{}".format(did) : {
+                        "basic" : {
+                              "driver" : "pmc-olt"
+                              }
+                        }
+                  },
+          }
+          ## Restart ONOS with cpqd driver config for OVS
+	  print('onos restart in setUpClass')
+          cls.start_onos(network_cfg = network_cfg)
+	  #status, code = OnosCtrl.config(network_cfg)
+          #if status is False:
+          #   log.info('JSON config request for app %s returned status %d' %(app, code))
+          #assert_equal(status, True)
+          #time.sleep(2)
+          cls.install_app_table()
+          cls.olt = OltConfig(olt_conf_file = cls.olt_conf_file)
+          OnosCtrl.cord_olt_config(cls.olt.olt_device_data())
+          cls.port_map, cls.port_list = cls.olt.olt_port_map()
+          cls.activate_apps(cls.apps + cls.olt_apps)
+
+      @classmethod
+      def tearDownClass(cls):
+          '''Deactivate the olt apps and restart OVS back'''
+          apps = cls.olt_apps + ( cls.table_app,)
+          for app in apps:
+              onos_ctrl = OnosCtrl(app)
+              onos_ctrl.deactivate()
+          cls.uninstall_app_table()
+          #cls.start_onos(network_cfg = {})
+
+      @classmethod
+      def activate_apps(cls, apps):
+            for app in apps:
+                  onos_ctrl = OnosCtrl(app)
+                  status, _ = onos_ctrl.activate()
+                  assert_equal(status, True)
+                  time.sleep(2)
+      @classmethod
+      def install_app_table(cls):
+            ##Uninstall the existing app if any
+            OnosCtrl.uninstall_app(cls.table_app)
+            time.sleep(2)
+            log.info('Installing the multi table app %s for subscriber test' %(cls.table_app_file))
+            OnosCtrl.install_app(cls.table_app_file)
+            time.sleep(3)
+            #onos_ctrl = OnosCtrl(cls.vtn_app)
+            #onos_ctrl.deactivate()
+
+      @classmethod
+      def uninstall_app_table(cls):
+            ##Uninstall the table app on class exit
+            OnosCtrl.uninstall_app(cls.table_app)
+            time.sleep(2)
+            log.info('Installing back the cord igmp app %s for subscriber test on exit' %(cls.app_file))
+            OnosCtrl.install_app(cls.app_file)
+            #onos_ctrl = OnosCtrl(cls.vtn_app)
+            #onos_ctrl.activate()
+
+      @classmethod
+      def start_onos(cls, network_cfg = None):
+            if cls.onos_restartable is False:
+                  log.info('ONOS restart is disabled. Skipping ONOS restart')
+                  return
+            if network_cfg is None:
+                  network_cfg = cls.device_dict
+
+            if type(network_cfg) is tuple:
+                  res = []
+                  for v in network_cfg:
+                        res += v.items()
+                  config = dict(res)
+            else:
+                  config = network_cfg
+            log.info('Restarting ONOS with new network configuration')
+            return cord_test_onos_restart(config = config)
+
+      @classmethod
+      def remove_onos_config(cls):
+            try:
+                  os.unlink('{}/network-cfg.json'.format(cls.onos_config_path))
+            except: pass
+      @classmethod
+      def start_cpqd(cls, mac = '00:11:22:33:44:55'):
+            dpid = mac.replace(':', '')
+            cpqd_file = os.sep.join( (cls.cpqd_path, 'cpqd.sh') )
+            cpqd_cmd = '{} {}'.format(cpqd_file, dpid)
+            ret = os.system(cpqd_cmd)
+            assert_equal(ret, 0)
+            time.sleep(10)
+            device_id = 'of:{}{}'.format('0'*4, dpid)
+            return device_id
+
+      @classmethod
+      def start_ovs(cls):
+            ovs_file = os.sep.join( (cls.ovs_path, 'of-bridge.sh') )
+            ret = os.system(ovs_file)
+            assert_equal(ret, 0)
+            time.sleep(30)
+
+      @classmethod
+      def ovs_cleanup(cls):
+            ##For every test case, delete all the OVS groups
+            cmd = 'ovs-ofctl del-groups br-int -OOpenFlow11 >/dev/null 2>&1'
+            cord_test_shell(cmd)
+            ##Since olt config is used for this test, we just fire a careless local cmd as well
+            try:
+                  os.system(cmd)
+            except: pass
+
+      def onos_aaa_load(self):
+            if self.aaa_loaded:
+                  return
+            aaa_dict = {'apps' : { 'org.onosproject.aaa' : { 'AAA' : { 'radiusSecret': 'radius_password',
+                                                                       'radiusIp': '172.17.0.2' } } } }
+            radius_ip = os.getenv('ONOS_AAA_IP') or '172.17.0.2'
+            aaa_dict['apps']['org.onosproject.aaa']['AAA']['radiusIp'] = radius_ip
+            self.onos_load_config('org.onosproject.aaa', aaa_dict)
+            self.aaa_loaded = True
+
+      def onos_dhcp_table_load(self, config = None):
+          dhcp_dict = {'apps' : { 'org.onosproject.dhcp' : { 'dhcp' : copy.copy(self.dhcp_server_config) } } }
+          dhcp_config = dhcp_dict['apps']['org.onosproject.dhcp']['dhcp']
+          if config:
+              for k in config.keys():
+                  if dhcp_config.has_key(k):
+                      dhcp_config[k] = config[k]
+          self.onos_load_config('org.onosproject.dhcp', dhcp_dict)
+
+      def onos_load_config(self, app, config):
+          status, code = OnosCtrl.config(config)
+          if status is False:
+             log.info('JSON config request for app %s returned status %d' %(app, code))
+             assert_equal(status, True)
+          time.sleep(2)
+      def dhcp_sndrcv(self, dhcp, update_seed = False):
+            cip, sip = dhcp.discover(update_seed = update_seed)
+            assert_not_equal(cip, None)
+            assert_not_equal(sip, None)
+            log.info('Got dhcp client IP %s from server %s for mac %s' %
+                     (cip, sip, dhcp.get_mac(cip)[0]))
+            return cip,sip
+
+      def dhcp_request(self, subscriber, seed_ip = '10.10.10.1', update_seed = False):
+            config = {'startip':'10.10.10.20', 'endip':'10.10.10.200',
+                      'ip':'10.10.10.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                      'subnet': '255.255.255.0', 'broadcast':'10.10.10.255', 'router':'10.10.10.1'}
+            self.onos_dhcp_table_load(config)
+            dhcp = DHCPTest(seed_ip = seed_ip, iface = subscriber.iface)
+            cip, sip = self.dhcp_sndrcv(dhcp, update_seed = update_seed)
+            return cip, sip
+
+      def recv_channel_cb(self, pkt):
+            ##First verify that we have received the packet for the joined instance
+            chan = self.subscriber.caddr(pkt[IP].dst)
+            assert_equal(chan in self.subscriber.join_map.keys(), True)
+            recv_time = monotonic.monotonic() * 1000000
+            join_time = self.subscriber.join_map[chan][self.subscriber.STATS_JOIN].start
+            delta = recv_time - join_time
+            self.subscriber.join_rx_stats.update(packets=1, t = delta, usecs = True)
+            self.subscriber.channel_update(chan, self.subscriber.STATS_RX, 1, t = delta)
+            log.debug('Packet received in %.3f usecs for group %s after join' %(delta, pkt[IP].dst))
+            self.test_status = True
+
+      def traffic_verify(self, subscriber):
+            if subscriber.has_service('TRAFFIC'):
+                  url = 'http://www.google.com'
+                  resp = requests.get(url)
+                  self.test_status = resp.ok
+                  if resp.ok == False:
+                        log.info('Subscriber %s failed get from url %s with status code %d'
+                                 %(subscriber.name, url, resp.status_code))
+                  else:
+                        log.info('GET request from %s succeeded for subscriber %s'
+                                 %(url, subscriber.name))
+                  return self.test_status
+
+      def tls_verify(self, subscriber):
+            if subscriber.has_service('TLS'):
+                  time.sleep(2)
+                  tls = TLSAuthTest(intf = subscriber.rx_intf)
+                  log.info('Running subscriber %s tls auth test' %subscriber.name)
+                  tls.runTest()
+                  self.test_status = True
+                  return self.test_status
+            else:
+                  self.test_status = True
+                  return self.test_status
+      def dhcp_verify(self, subscriber):
+            if subscriber.has_service('DHCP'):
+                  cip, sip = self.dhcp_request(subscriber, update_seed = True)
+                  log.info('Subscriber %s got client ip %s from server %s' %(subscriber.name, cip, sip))
+                  subscriber.src_list = [cip]
+                  self.test_status = True
+                  return self.test_status
+            else:
+                  subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+                  self.test_status = True
+                  return self.test_status
+
+      def dhcp_jump_verify(self, subscriber):
+            if subscriber.has_service('DHCP'):
+                  cip, sip = self.dhcp_request(subscriber, seed_ip = '10.10.200.1')
+                  log.info('Subscriber %s got client ip %s from server %s' %(subscriber.name, cip, sip))
+                  subscriber.src_list = [cip]
+                  self.test_status = True
+                  return self.test_status
+            else:
+                  subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+                  self.test_status = True
+                  return self.test_status
+
+      def dhcp_next_verify(self, subscriber):
+            if subscriber.has_service('DHCP'):
+                  cip, sip = self.dhcp_request(subscriber, seed_ip = '10.10.150.1')
+                  log.info('Subscriber %s got client ip %s from server %s' %(subscriber.name, cip, sip))
+                  subscriber.src_list = [cip]
+                  self.test_status = True
+                  return self.test_status
+            else:
+                  subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+                  self.test_status = True
+                  return self.test_status
+      def igmp_verify(self, subscriber):
+            chan = 0
+            if subscriber.has_service('IGMP'):
+                  ##We wait for all the subscribers to join before triggering leaves
+                  if subscriber.rx_port > 1:
+                        time.sleep(5)
+                  subscriber.channel_join(chan, delay = 0)
+                  self.num_joins += 1
+                  while self.num_joins < self.num_subscribers:
+                        time.sleep(5)
+                  log.info('All subscribers have joined the channel')
+                  for i in range(10):
+                        subscriber.channel_receive(chan, cb = subscriber.recv_channel_cb, count = 10)
+                        log.info('Leaving channel %d for subscriber %s' %(chan, subscriber.name))
+                        subscriber.channel_leave(chan)
+                        time.sleep(5)
+                        log.info('Interface %s Join RX stats for subscriber %s, %s' %(subscriber.iface, subscriber.name,subscriber.join_rx_stats))
+                        #Should not receive packets for this subscriber
+                        self.recv_timeout = True
+                        subscriber.recv_timeout = True
+                        subscriber.channel_receive(chan, cb = subscriber.recv_channel_cb, count = 10)
+                        subscriber.recv_timeout = False
+                        self.recv_timeout = False
+                        log.info('Joining channel %d for subscriber %s' %(chan, subscriber.name))
+                        subscriber.channel_join(chan, delay = 0)
+                  self.test_status = True
+                  return self.test_status
+
+      def igmp_jump_verify(self, subscriber):
+            if subscriber.has_service('IGMP'):
+                  for i in xrange(subscriber.num):
+                        log.info('Subscriber %s jumping channel' %subscriber.name)
+                        chan = subscriber.channel_jump(delay=0)
+                        subscriber.channel_receive(chan, cb = subscriber.recv_channel_cb, count = 1)
+                        log.info('Verified receive for channel %d, subscriber %s' %(chan, subscriber.name))
+                        time.sleep(3)
+                  log.info('Interface %s Jump RX stats for subscriber %s, %s' %(subscriber.iface, subscriber.name, subscriber.join_rx_stats))
+                  self.test_status = True
+                  return self.test_status
+      def igmp_next_verify(self, subscriber):
+            if subscriber.has_service('IGMP'):
+                  for i in xrange(subscriber.num):
+                        if i:
+                              chan = subscriber.channel_join_next(delay=0)
+                        else:
+                              chan = subscriber.channel_join(i, delay=0)
+                        log.info('Joined next channel %d for subscriber %s' %(chan, subscriber.name))
+                        subscriber.channel_receive(chan, cb = subscriber.recv_channel_cb, count=1)
+                        log.info('Verified receive for channel %d, subscriber %s' %(chan, subscriber.name))
+                        time.sleep(3)
+                  log.info('Interface %s Join Next RX stats for subscriber %s, %s' %(subscriber.iface, subscriber.name, subscriber.join_rx_stats))
+                  self.test_status = True
+                  return self.test_status
+
+      def generate_port_list(self, subscribers, channels):
+            return self.port_list[:subscribers]
+
+      def subscriber_load(self, create = True, num = 10, num_channels = 1, channel_start = 0, port_list = []):
+            '''Load the subscriber from the database'''
+            self.subscriber_db = SubscriberDB(create = create, services = self.test_services)
+            if create is True:
+                  self.subscriber_db.generate(num)
+            self.subscriber_info = self.subscriber_db.read(num)
+            self.subscriber_list = []
+            if not port_list:
+                  port_list = self.generate_port_list(num, num_channels)
+
+            index = 0
+            for info in self.subscriber_info:
+                  self.subscriber_list.append(Subscriber(name=info['Name'],
+                                                         service=info['Service'],
+                                                         port_map = self.port_map,
+                                                         num=num_channels,
+                                                         channel_start = channel_start,
+                                                         tx_port = port_list[index][0],
+                                                         rx_port = port_list[index][1]))
+                  if num_channels > 1:
+                        channel_start += num_channels
+                  index += 1
+
+            #load the ssm list for all subscriber channels
+            igmpChannel = IgmpChannel()
+            ssm_groups = map(lambda sub: sub.channels, self.subscriber_list)
+            ssm_list = reduce(lambda ssm1, ssm2: ssm1+ssm2, ssm_groups)
+            igmpChannel.igmp_load_ssm_config(ssm_list)
+      def subscriber_join_verify( self, num_subscribers = 10, num_channels = 1,
+                                  channel_start = 0, cbs = None, port_list = [], negative_subscriber_auth = None):
+          self.test_status = False
+          self.ovs_cleanup()
+          subscribers_count = num_subscribers
+          sub_loop_count =  num_subscribers
+          self.subscriber_load(create = True, num = num_subscribers,
+                               num_channels = num_channels, channel_start = channel_start, port_list = port_list)
+          self.onos_aaa_load()
+          self.thread_pool = ThreadPool(min(100, subscribers_count), queue_size=1, wait_timeout=1)
+
+          chan_leave = False #for single channel, multiple subscribers
+          if None in (cbs, negative_subscriber_auth):
+                cbs = (self.tls_verify, self.dhcp_verify, self.igmp_verify, self.traffic_verify)
+                chan_leave = True
+          cbs_negative = cbs
+          for subscriber in self.subscriber_list:
+                subscriber.start()
+                if negative_subscriber_auth is 'half' and sub_loop_count%2 is not 0:
+                   cbs = (self.tls_verify, self.dhcp_verify, self.igmp_verify, self.traffic_verify)
+                elif negative_subscriber_auth is 'onethird' and sub_loop_count%3 is not 0:
+                   cbs = (self.tls_verify, self.dhcp_verify, self.igmp_verify, self.traffic_verify)
+                else:
+                   cbs = cbs_negative
+                sub_loop_count = sub_loop_count - 1
+                pool_object = subscriber_pool(subscriber, cbs)
+                self.thread_pool.addTask(pool_object.pool_cb)
+          self.thread_pool.cleanUpThreads()
+          for subscriber in self.subscriber_list:
+                subscriber.stop()
+                if chan_leave is True:
+                      subscriber.channel_leave(0)
+          subscribers_count = 0
+          return self.test_status
+      def tls_invalid_cert(self, subscriber):
+          if subscriber.has_service('TLS'):
+             time.sleep(2)
+             log.info('Running subscriber %s tls auth test' %subscriber.name)
+             tls = TLSAuthTest(client_cert = self.CLIENT_CERT_INVALID)
+             tls.runTest()
+             if tls.failTest == True:
+                self.test_status = False
+             return self.test_status
+          else:
+              self.test_status = True
+              return self.test_status
+
+      def tls_no_cert(self, subscriber):
+          if subscriber.has_service('TLS'):
+             time.sleep(2)
+             log.info('Running subscriber %s tls auth test' %subscriber.name)
+             tls = TLSAuthTest(client_cert = '')
+             tls.runTest()
+             if tls.failTest == True:
+                self.test_status = False
+             return self.test_status
+          else:
+              self.test_status = True
+              return self.test_status
+
+      def tls_self_signed_cert(self, subscriber):
+          if subscriber.has_service('TLS'):
+             time.sleep(2)
+             log.info('Running subscriber %s tls auth test' %subscriber.name)
+             tls = TLSAuthTest(client_cert = self.CLIENT_CERT)
+             tls.runTest()
+             if tls.failTest == False:
+                self.test_status = True
+             return self.test_status
+          else:
+              self.test_status = True
+              return self.test_status
+
+      def tls_non_ca_authrized_cert(self, subscriber):
+          if subscriber.has_service('TLS'):
+             time.sleep(2)
+             log.info('Running subscriber %s tls auth test' %subscriber.name)
+             tls = TLSAuthTest(client_cert = self.CLIENT_CERT_NON_CA_AUTHORIZED)
+             tls.runTest()
+             if tls.failTest == False:
+                self.test_status = True
+             return self.test_status
+          else:
+              self.test_status = True
+              return self.test_status
+      def tls_Nsubscribers_use_same_valid_cert(self, subscriber):
+          if subscriber.has_service('TLS'):
+             time.sleep(2)
+             log.info('Running subscriber %s tls auth test' %subscriber.name)
+             num_users = 3
+             for i in xrange(num_users):
+                 tls = TLSAuthTest(intf = 'veth{}'.format(i*2))
+                 tls.runTest()
+             if tls.failTest == False:
+                self.test_status = True
+             return self.test_status
+          else:
+              self.test_status = True
+              return self.test_status
+
+      def dhcp_discover_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+             time.sleep(2)
+             log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+             t1 = self.subscriber_dhcp_1release()
+             self.test_status = True
+             return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+
+      def subscriber_dhcp_1release(self, iface = INTF_RX_DEFAULT):
+             config = {'startip':'10.10.100.20', 'endip':'10.10.100.21',
+                       'ip':'10.10.100.2', 'mac': "ca:fe:ca:fe:8a:fe",
+                       'subnet': '255.255.255.0', 'broadcast':'10.10.100.255', 'router':'10.10.100.1'}
+             self.onos_dhcp_table_load(config)
+             self.dhcp = DHCPTest(seed_ip = '10.10.100.10', iface = iface)
+             cip, sip = self.send_recv()
+             log.info('Releasing ip %s to server %s' %(cip, sip))
+             assert_equal(self.dhcp.release(cip), True)
+             log.info('Triggering DHCP discover again after release')
+             cip2, sip2 = self.send_recv(update_seed = True)
+             log.info('Verifying released IP was given back on rediscover')
+             assert_equal(cip, cip2)
+             log.info('Test done. Releasing ip %s to server %s' %(cip2, sip2))
+             assert_equal(self.dhcp.release(cip2), True)
+      def dhcp_client_reboot_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                  time.sleep(2)
+                  log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                  tl = self.subscriber_dhcp_client_request_after_reboot()
+                  self.test_status = True
+                  return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+
+      def subscriber_dhcp_client_request_after_reboot(self, iface = INTF_RX_DEFAULT):
+          #''' Client sends DHCP Request after reboot.'''
+
+          config = {'startip':'20.20.20.30', 'endip':'20.20.20.69',
+                   'ip':'20.20.20.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                   'subnet': '255.255.255.0', 'broadcast':'20.20.20.255', 'router':'20.20.20.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '20.20.20.45', iface = iface)
+          cip, sip, mac, lval = self.dhcp.only_discover()
+          log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+
+          log.info("Verifying Client 's IP and mac in DHCP Offer packet. Those should not be none, which is expected.")
+
+          if (cip == None and mac != None):
+                log.info("Verified that Client 's IP and mac in DHCP Offer packet are none, which is not expected behavior.")
+                assert_not_equal(cip, None)
+
+          else:
+                new_cip, new_sip = self.dhcp.only_request(cip, mac)
+                if new_cip == None:
+                        log.info("Got DHCP server NAK.")
+                os.system('ifconfig '+iface+' down')
+                log.info('Client goes down.')
+                log.info('Delay for 5 seconds.')
+
+                time.sleep(5)
+
+                os.system('ifconfig '+iface+' up')
+                log.info('Client is up now.')
+
+                new_cip, new_sip = self.dhcp.only_request(cip, mac)
+                if new_cip == None:
+                        log.info("Got DHCP server NAK.")
+                        assert_not_equal(new_cip, None)
+                elif new_cip != None:
+                        log.info("Got DHCP ACK.")
+      def dhcp_client_renew_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_client_renew_time()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+
+      def subscriber_dhcp_client_renew_time(self, iface = INTF_RX_DEFAULT):
+          config = {'startip':'20.20.20.30', 'endip':'20.20.20.69',
+                   'ip':'20.20.20.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                   'subnet': '255.255.255.0', 'broadcast':'20.20.20.255', 'router':'20.20.20.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '20.20.20.45', iface = iface)
+          cip, sip, mac , lval = self.dhcp.only_discover()
+          log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+
+          log.info("Verifying Client 's IP and mac in DHCP Offer packet. Those should not be none, which is expected.")
+          if (cip == None and mac != None):
+                log.info("Verified that Client 's IP and mac in DHCP Offer packet are none, which is not expected behavior.")
+                assert_not_equal(cip, None)
+          elif cip and sip and mac:
+                log.info("Triggering DHCP Request.")
+                new_cip, new_sip, lval = self.dhcp.only_request(cip, mac, renew_time = True)
+                if new_cip and new_sip and lval:
+                        log.info("Client 's Renewal time is :%s",lval)
+                        log.info("Generating delay till renewal time.")
+                        time.sleep(lval)
+                        log.info("Client Sending Unicast DHCP request.")
+                        latest_cip, latest_sip = self.dhcp.only_request(new_cip, mac, unicast = True)
+                        if latest_cip and latest_sip:
+                                log.info("Got DHCP Ack. Lease Renewed for ip %s and mac %s from server %s." %
+                                                (latest_cip, mac, latest_sip) )
+
+                        elif latest_cip == None:
+                                log.info("Got DHCP NAK. Lease not renewed.")
+                elif new_cip == None or new_sip == None or lval == None:
+                        log.info("Got DHCP NAK.")
+      def dhcp_server_reboot_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_server_after_reboot()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+      def subscriber_dhcp_server_after_reboot(self, iface = INTF_RX_DEFAULT):
+          ''' DHCP server goes down.'''
+          config = {'startip':'20.20.20.30', 'endip':'20.20.20.69',
+                   'ip':'20.20.20.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                   'subnet': '255.255.255.0', 'broadcast':'20.20.20.255', 'router':'20.20.20.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '20.20.20.45', iface = iface)
+          cip, sip, mac, lval = self.dhcp.only_discover()
+          log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+          log.info("Verifying Client 's IP and mac in DHCP Offer packet. Those should not be none, which is expected.")
+          if (cip == None and mac != None):
+                log.info("Verified that Client 's IP and mac in DHCP Offer packet are none, which is not expected behavior.")
+                assert_not_equal(cip, None)
+          else:
+                new_cip, new_sip = self.dhcp.only_request(cip, mac)
+                if new_cip == None:
+                        log.info("Got DHCP server NAK.")
+                        assert_not_equal(new_cip, None)
+                log.info('Getting DHCP server Down.')
+                onos_ctrl = OnosCtrl(self.dhcp_app)
+                onos_ctrl.deactivate()
+                for i in range(0,4):
+                        log.info("Sending DHCP Request.")
+                        log.info('')
+                        new_cip, new_sip = self.dhcp.only_request(cip, mac)
+                        if new_cip == None and new_sip == None:
+                                log.info('')
+                                log.info("DHCP Request timed out.")
+                        elif new_cip and new_sip:
+                                log.info("Got Reply from DHCP server.")
+                                assert_equal(new_cip,None) #Neagtive Test Case
+                log.info('Getting DHCP server Up.')
+#               self.activate_apps(self.dhcp_app)
+                onos_ctrl = OnosCtrl(self.dhcp_app)
+                status, _ = onos_ctrl.activate()
+                assert_equal(status, True)
+                time.sleep(3)
+                for i in range(0,4):
+                        log.info("Sending DHCP Request after DHCP server is up.")
+                        log.info('')
+                        new_cip, new_sip = self.dhcp.only_request(cip, mac)
+                        if new_cip == None and new_sip == None:
+                                log.info('')
+                                log.info("DHCP Request timed out.")
+                        elif new_cip and new_sip:
+                                log.info("Got Reply from DHCP server.")
+                                assert_equal(new_cip,None) #Neagtive Test Case
+      def dhcp_client_rebind_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_client_rebind_time()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+
+      def subscriber_dhcp_client_rebind_time(self, iface = INTF_RX_DEFAULT):
+          config = {'startip':'20.20.20.30', 'endip':'20.20.20.69',
+                   'ip':'20.20.20.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                   'subnet': '255.255.255.0', 'broadcast':'20.20.20.255', 'router':'20.20.20.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '20.20.20.45', iface = iface)
+          cip, sip, mac, lval = self.dhcp.only_discover()
+          log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+          log.info("Verifying Client 's IP and mac in DHCP Offer packet. Those should not be none, which is expected.")
+          if (cip == None and mac != None):
+                log.info("Verified that Client 's IP and mac in DHCP Offer packet are none, which is not expected behavior.")
+                assert_not_equal(cip, None)
+          elif cip and sip and mac:
+                log.info("Triggering DHCP Request.")
+                new_cip, new_sip, lval = self.dhcp.only_request(cip, mac, rebind_time = True)
+                if new_cip and new_sip and lval:
+                        log.info("Client 's Rebind time is :%s",lval)
+                        log.info("Generating delay till rebind time.")
+                        time.sleep(lval)
+                        log.info("Client Sending broadcast DHCP requests for renewing lease or for getting new ip.")
+                        self.dhcp.after_T2 = True
+                        for i in range(0,4):
+                                latest_cip, latest_sip = self.dhcp.only_request(new_cip, mac)
+                                if latest_cip and latest_sip:
+                                        log.info("Got DHCP Ack. Lease Renewed for ip %s and mac %s from server %s." %
+                                                        (latest_cip, mac, latest_sip) )
+                                        break
+                                elif latest_cip == None:
+                                        log.info("Got DHCP NAK. Lease not renewed.")
+                        assert_not_equal(latest_cip, None)
+                elif new_cip == None or new_sip == None or lval == None:
+                        log.info("Got DHCP NAK.Lease not Renewed.")
+      def dhcp_starvation_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_starvation()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+
+      def subscriber_dhcp_starvation(self, iface = INTF_RX_DEFAULT):
+          '''DHCP starve'''
+          config = {'startip':'182.17.0.20', 'endip':'182.17.0.69',
+                    'ip':'182.17.0.2', 'mac': "ca:fe:c3:fe:ca:fe",
+                    'subnet': '255.255.255.0', 'broadcast':'182.17.0.255', 'router':'182.17.0.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '182.17.0.1', iface = iface)
+          log.info('Verifying 1 ')
+          for x in xrange(50):
+              mac = RandMAC()._fix()
+              self.send_recv(mac = mac)
+          log.info('Verifying 2 ')
+          cip, sip = self.send_recv(update_seed = True, validate = False)
+          assert_equal(cip, None)
+          assert_equal(sip, None)
+
+      def dhcp_same_client_multi_discovers_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_same_client_multiple_discover()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+      def subscriber_dhcp_same_client_multiple_discover(self, iface = INTF_RX_DEFAULT):
+          ''' DHCP Client sending multiple discover . '''
+          config = {'startip':'10.10.10.20', 'endip':'10.10.10.69',
+                    'ip':'10.10.10.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                    'subnet': '255.255.255.0', 'broadcast':'10.10.10.255', 'router':'10.10.10.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '10.10.10.1', iface = iface)
+          cip, sip, mac, lval = self.dhcp.only_discover()
+          log.info('Got dhcp client IP %s from server %s for mac %s . Not going to send DHCPREQUEST.' %
+                  (cip, sip, mac) )
+          log.info('Triggering DHCP discover again.')
+          new_cip, new_sip, new_mac , lval = self.dhcp.only_discover()
+          if cip == new_cip:
+                 log.info('Got same ip for 2nd DHCP discover for client IP %s from server %s for mac %s. Triggering DHCP Request. '
+                          % (new_cip, new_sip, new_mac) )
+          elif cip != new_cip:
+                log.info('Ip after 1st discover %s' %cip)
+                log.info('Map after 2nd discover %s' %new_cip)
+                assert_equal(cip, new_cip)
+
+      def dhcp_same_client_multi_request_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_same_client_multiple_request()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+      def subscriber_dhcp_same_client_multiple_request(self, iface = INTF_RX_DEFAULT):
+          ''' DHCP Client sending multiple repeat DHCP requests. '''
+          config = {'startip':'10.10.10.20', 'endip':'10.10.10.69',
+                    'ip':'10.10.10.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                    'subnet': '255.255.255.0', 'broadcast':'10.10.10.255', 'router':'10.10.10.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '10.10.10.1', iface = iface)
+          log.info('Sending DHCP discover and DHCP request.')
+          cip, sip = self.send_recv()
+          mac = self.dhcp.get_mac(cip)[0]
+          log.info("Sending DHCP request again.")
+          new_cip, new_sip = self.dhcp.only_request(cip, mac)
+          if (new_cip,new_sip) == (cip,sip):
+                log.info('Got same ip for 2nd DHCP Request for client IP %s from server %s for mac %s.'
+                          % (new_cip, new_sip, mac) )
+          elif (new_cip,new_sip):
+                log.info('No DHCP ACK')
+                assert_equal(new_cip, None)
+                assert_equal(new_sip, None)
+          else:
+                print "Something went wrong."
+
+      def dhcp_client_desired_ip_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_client_desired_address()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+
+      def subscriber_dhcp_client_desired_address(self, iface = INTF_RX_DEFAULT):
+          '''DHCP Client asking for desired IP address.'''
+          config = {'startip':'20.20.20.30', 'endip':'20.20.20.69',
+                   'ip':'20.20.20.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                   'subnet': '255.255.255.0', 'broadcast':'20.20.20.255', 'router':'20.20.20.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '20.20.20.31', iface = iface)
+          cip, sip, mac , lval = self.dhcp.only_discover(desired = True)
+          log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+          if cip == self.dhcp.seed_ip:
+                log.info('Got dhcp client IP %s from server %s for mac %s as desired .' %
+                  (cip, sip, mac) )
+          elif cip != self.dhcp.seed_ip:
+                log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+                log.info('The desired ip was: %s .' % self.dhcp.seed_ip)
+                assert_equal(cip, self.dhcp.seed_ip)
+      def dhcp_client_request_pkt_with_non_offered_ip_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_server_nak_packet()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+
+      def subscriber_dhcp_server_nak_packet(self, iface = INTF_RX_DEFAULT):
+          config = {'startip':'20.20.20.30', 'endip':'20.20.20.69',
+                   'ip':'20.20.20.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                   'subnet': '255.255.255.0', 'broadcast':'20.20.20.255', 'router':'20.20.20.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '20.20.20.45', iface = iface)
+          cip, sip, mac, lval = self.dhcp.only_discover()
+          log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+          log.info("Verifying Client 's IP and mac in DHCP Offer packet. Those should not be none, which is expected.")
+          if (cip == None and mac != None):
+                log.info("Verified that Client 's IP and mac in DHCP Offer packet are none, which is not expected behavior.")
+                assert_not_equal(cip, None)
+          else:
+                new_cip, new_sip = self.dhcp.only_request('20.20.20.31', mac)
+                if new_cip == None:
+                        log.info("Got DHCP server NAK.")
+                        assert_equal(new_cip, None)  #Negative Test Case
+
+      def dhcp_client_requested_out_pool_ip_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_client_desired_address_out_of_pool()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+      def subscriber_dhcp_client_desired_address_out_of_pool(self, iface = INTF_RX_DEFAULT):
+          '''DHCP Client asking for desired IP address from out of pool.'''
+          config = {'startip':'20.20.20.30', 'endip':'20.20.20.69',
+                   'ip':'20.20.20.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                   'subnet': '255.255.255.0', 'broadcast':'20.20.20.255', 'router':'20.20.20.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '20.20.20.35', iface = iface)
+          cip, sip, mac, lval = self.dhcp.only_discover(desired = True)
+          log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+          if cip == self.dhcp.seed_ip:
+                log.info('Got dhcp client IP %s from server %s for mac %s as desired .' %
+                  (cip, sip, mac) )
+                assert_equal(cip, self.dhcp.seed_ip) #Negative Test Case
+
+          elif cip != self.dhcp.seed_ip:
+                log.info('Got dhcp client IP %s from server %s for mac %s .' %
+                  (cip, sip, mac) )
+                log.info('The desired ip was: %s .' % self.dhcp.seed_ip)
+                assert_not_equal(cip, self.dhcp.seed_ip)
+
+          elif cip == None:
+                log.info('Got DHCP NAK')
+
+      def dhcp_client_specific_lease_scenario(self, subscriber):
+          if subscriber.has_service('DHCP'):
+                time.sleep(2)
+                log.info('Running subscriber %s DHCP rediscover scenario test' %subscriber.name)
+                tl = self.subscriber_dhcp_specific_lease_packet()
+                self.test_status = True
+                return self.test_status
+          else:
+              subscriber.src_list = ['10.10.10.{}'.format(subscriber.rx_port)]
+              self.test_status = True
+              return self.test_status
+
+      def subscriber_dhcp_specific_lease_packet(self, iface = INTF_RX_DEFAULT):
+          ''' Client sends DHCP Discover packet for particular lease time.'''
+          config = {'startip':'20.20.20.30', 'endip':'20.20.20.69',
+                   'ip':'20.20.20.2', 'mac': "ca:fe:ca:fe:ca:fe",
+                   'subnet': '255.255.255.0', 'broadcast':'20.20.20.255', 'router':'20.20.20.1'}
+          self.onos_dhcp_table_load(config)
+          self.dhcp = DHCPTest(seed_ip = '20.20.20.45', iface = iface)
+          log.info('Sending DHCP discover with lease time of 700')
+          cip, sip, mac, lval = self.dhcp.only_discover(lease_time = True)
+
+          log.info("Verifying Client 's IP and mac in DHCP Offer packet.")
+          if (cip == None and mac != None):
+                log.info("Verified that Client 's IP and mac in DHCP Offer packet are none, which is not expected behavior.")
+                assert_not_equal(cip, None)
+          elif lval != 700:
+                log.info('Getting dhcp client IP %s from server %s for mac %s with lease time %s. That is not 700.' %
+                         (cip, sip, mac, lval) )
+                assert_not_equal(lval, 700)
+