Test-Scale:
    Merging latency tests of control packets
    and some scale tests related to IGMP ,validating CPU usage.

Change-Id: Ie1357ed779c07c84883ff431069d4c88be0c59d8
diff --git a/src/test/scale/scaleTest.py b/src/test/scale/scaleTest.py
index d94655f..8a96d01 100644
--- a/src/test/scale/scaleTest.py
+++ b/src/test/scale/scaleTest.py
@@ -17,13 +17,17 @@
 import sys
 import json
 import requests
+import random
 from nose.tools import *
+from scapy.all import *
 from twisted.internet import defer
 from nose.twistedtools import reactor, deferred
 from CordTestUtils import *
 from OltConfig import OltConfig
 from onosclidriver import OnosCliDriver
 from SSHTestAgent import SSHTestAgent
+from Channels import IgmpChannel
+from IGMP import *
 from CordLogger import CordLogger
 from VSGAccess import VSGAccess
 from CordTestUtils import log_test as log
@@ -51,6 +55,25 @@
     restore_methods = []
     TIMEOUT=120
     NUM_SUBSCRIBERS = 100
+    wan_intf_ip = '10.6.1.129'
+    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'
+    igmp_eth = Ether(dst = IGMP_DST_MAC, type = ETH_P_IP)
+    igmp_ip = IP(dst = IP_DST)
+    PORT_TX_DEFAULT = 2
+    PORT_RX_DEFAULT = 1
+    igmp_app = 'org.opencord.igmp'
+
 
     @classmethod
     def getSubscriberCredentials(cls, subId):
@@ -203,7 +226,7 @@
         cls.vcpe_container = vcpe_container_reserved or vcpe_container
         cls.vcpe_dhcp = vcpe_dhcp_reserved or vcpe_dhcp
         VSGAccess.setUp()
-        cls.setUpCordApi()
+        #cls.setUpCordApi()
         if cls.on_pod is True:
             cls.openVCPEAccess(cls.volt_subscriber_info)
 
@@ -222,6 +245,14 @@
         for restore_method in cls.restore_methods:
             restore_method()
 
+    def get_system_cpu_usage(self):
+        """ Getting compute node CPU usage """
+        ssh_agent = SSHTestAgent(host = self.HEAD_NODE, user = self.USER, password = self.PASS)
+        cmd = "top -b -n1 | grep 'Cpu(s)' | awk '{print $2 + $4}'"
+        status, output = ssh_agent.run_cmd(cmd)
+        assert_equal(status, True)
+        return float(output)
+
     def vsg_xos_subscriber_id(self, index):
 	log.info('index and its type are %s, %s'%(index, type(index)))
         volt_subscriber_info = self.volt_subscriber_info[index]
@@ -241,7 +272,7 @@
         c_tag = int(volt_subscriber_info['voltTenant']['c_tag'])
         vcpe = 'vcpe-{}-{}'.format(s_tag, c_tag)
         log.info('Creating tenant with s_tag: %d, c_tag: %d' %(s_tag, c_tag))
-        subId = ''
+        """subId = ''
         try:
             result = self.restApiXos.ApiPost('TENANT_SUBSCRIBER', subscriber_info)
             assert_equal(result, True)
@@ -265,7 +296,7 @@
             log.info('Testing for external connectivity to VCPE %s' %(vcpe))
             self.vsg_for_external_connectivity(index)
         finally:
-            return subId
+            return subId"""
 
     def vsg_xos_subscriber_delete(self, index, subId = '', voltId = '', subscriber_info = None, volt_subscriber_info = None):
         if self.on_pod is False:
@@ -297,6 +328,108 @@
         log.info('Deleting VOLT Tenant ID %s for subscriber %s' %(voltId, subId))
         self.restApiXos.ApiDelete('TENANT_VOLT', voltId)
 
+    def onos_load_config(self, config):
+        #log_test.info('onos load config is %s'%config)
+        status, code = OnosCtrl.config(config)
+        if status is False:
+            log_test.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.opencord.igmp' : { 'ssmTranslate' : [] } } }
+          ssm_xlate_list = ssm_dict['apps']['org.opencord.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)
+          IgmpChannel().cord_port_table_load(cord_port_map)
+          time.sleep(2)
+
+    def generate_random_multicast_ip_addresses(self,count=500):
+        multicast_ips = []
+        while(count >= 1):
+                ip = '.'.join([str(random.randint(224,239)),str(random.randint(1,254)),str(random.randint(1,254)),str(random.randint(1,254))])
+                if ip in multicast_ips:
+                    pass
+                else:
+                    multicast_ips.append(ip)
+                    count -= 1
+        return multicast_ips
+
+    def generate_random_unicast_ip_addresses(self,count=500):
+        unicast_ips = []
+        while(count >= 1):
+                ip = '.'.join([str(random.randint(11,126)),str(random.randint(1,254)),str(random.randint(1,254)),str(random.randint(1,254))])
+                if ip in unicast_ips:
+                    pass
+                else:
+                    unicast_ips.append(ip)
+                    count -= 1
+        return unicast_ips
+
+    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 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)
+        log.info('sending igmp join packet %s'%pkt.show())
+        sendp(pkt, iface=iface)
+        time.sleep(delay)
+
+    def send_multicast_data_traffic(self, group, intf= 'veth2',source = '1.2.3.4'):
+        dst_mac = self.iptomac(group)
+        eth = Ether(dst= dst_mac)
+        ip = IP(dst=group,src=source)
+        data = repr(monotonic.monotonic())
+        sendp(eth/ip/data,count=20, iface = intf)
+
+    def verify_igmp_data_traffic(self, group, intf='veth0', source='1.2.3.4' ):
+        log_test.info('verifying multicast traffic for group %s from source %s'%(group,source))
+        self.success = False
+        def recv_task():
+            def igmp_recv_cb(pkt):
+                #log_test.info('received multicast data packet is %s'%pkt.show())
+                log_test.info('multicast data received for group %s from source %s'%(group,source))
+                self.success = True
+            sniff(prn = igmp_recv_cb,lfilter = lambda p: IP in p and p[IP].dst == group and p[IP].src == source, count=1,timeout = 2, iface='veth0')
+        t = threading.Thread(target = recv_task)
+        t.start()
+        self.send_multicast_data_traffic(group,source=source)
+        t.join()
+        return self.success
+
     def test_scale_for_vsg_vm_creations(self):
         for index in xrange(len(self.subscriber_info)):
             #check if the index exists
@@ -322,6 +455,16 @@
         if subId and subId != '0':
             self.vsg_xos_subscriber_delete(100, subId)
 
+    def test_scale_of_subcriber_vcpe_creations_in_multiple_vsg_vm(self):
+        subId = self.vsg_xos_subscriber_create(100)
+        if subId and subId != '0':
+            self.vsg_xos_subscriber_delete(100, subId)
+
+    def test_scale_of_subcriber_vcpe_creations_with_one_vcpe_in_one_vsg_vm(self):
+        subId = self.vsg_xos_subscriber_create(100)
+        if subId and subId != '0':
+            self.vsg_xos_subscriber_delete(100, subId)
+
     def test_scale_for_cord_subscriber_creation_and_deletion(self):
         subId = self.vsg_xos_subscriber_create(100)
         if subId and subId != '0':
@@ -330,3 +473,105 @@
     def test_cord_for_scale_of_subscriber_containers_per_compute_node(self):
         pass
 
+    def test_latency_of_cord_for_control_packets_using_icmp_packet(self):
+        cmd = "ping -c 4 {0} | tail -1| awk '{{print $4}}'".format(self.wan_intf_ip)
+        st, out = getstatusoutput(cmd)
+        if out != '':
+                out = out.split('/')
+                avg_rtt = out[1]
+                latency = float(avg_rtt)/float(2)
+        else:
+            latency = None
+        log.info('CORD setup latency calculated from icmp packet is = %s ms'%latency)
+        assert_not_equal(latency,None)
+
+    def test_latency_of_cord_for_control_packets_using_increasing_sizes_of_icmp_packet(self):
+        pckt_sizes = [100,500,1000,1500]
+        for size in pckt_sizes:
+            cmd = "ping -c 4 -s {} {} | tail -1| awk '{{print $4}}'".format(size,self.wan_intf_ip)
+            st, out = getstatusoutput(cmd)
+            if out != '':
+                out = out.split('/')
+                avg_rtt = out[1]
+                latency = float(avg_rtt)/float(2)
+            else:
+                latency = None
+            log.info('CORD setup latency calculated from icmp packet with size %s bytes is = %s ms'%(size,latency))
+            assert_not_equal(latency,None)
+
+    def test_latency_of_cord_with_traceroute(self):
+        cmd = "traceroute -q1 {} | tail -1| awk '{{print $4}}'".format(self.wan_intf_ip)
+        avg_rtt = float(0)
+        latency = None
+        for index in [1,2,3]:
+            st, out = getstatusoutput(cmd)
+            if out != '':
+                avg_rtt += float(out)
+        latency = float(avg_rtt)/float(6)
+        log.info('CORD setup latency calculated from  traceroute is = %s ms'%latency)
+        assert_not_equal(latency,0.0)
+
+    def test_scale_with_igmp_joins_for_500_multicast_groups_and_check_cpu_usage(self, group_count=500):
+        OnosCtrl(self.igmp_app).activate()
+        groups = self.generate_random_multicast_ip_addresses(count = group_count)
+        sources = self.generate_random_unicast_ip_addresses(count = group_count)
+        self.onos_ssm_table_load(groups,src_list=sources,flag=True)
+        for index in range(group_count):
+            self.send_igmp_join(groups = [groups[index]], src_list = [sources[index]],record_type = IGMP_V3_GR_TYPE_INCLUDE,
+                                         iface = self.V_INF1)
+            status = self.verify_igmp_data_traffic(groups[index],intf=self.V_INF1,source=sources[index])
+            assert_equal(status, True)
+            log_test.info('data received for group %s from source %s - %d'%(groups[index],sources[index],index))
+            if index % 50 == 0:
+                cpu_usage = self.get_system_cpu_usage()
+                log.info('CPU usage is %s for multicast group entries %s'%(cpu_usage,index+1))
+
+    def test_scale_with_igmp_joins_for_1000_multicast_groups_and_check_cpu_usage(self, group_count=1000):
+        OnosCtrl(self.igmp_app).activate()
+        groups = self.generate_random_multicast_ip_addresses(count = group_count)
+        sources = self.generate_random_unicast_ip_addresses(count = group_count)
+        self.onos_ssm_table_load(groups,src_list=sources,flag=True)
+        for index in range(group_count):
+            self.send_igmp_join(groups = [groups[index]], src_list = [sources[index]],record_type = IGMP_V3_GR_TYPE_INCLUDE,
+                                         iface = self.V_INF1)
+            status = self.verify_igmp_data_traffic(groups[index],intf=self.V_INF1,source=sources[index])
+            assert_equal(status, True)
+            log_test.info('data received for group %s from source %s - %d'%(groups[index],sources[index],index))
+            if index % 50 == 0:
+                cpu_usage = self.get_system_cpu_usage()
+                log.info('CPU usage is %s for multicast group entries %s'%(cpu_usage,index+1))
+
+    def test_scale_with_igmp_joins_for_2000_multicast_groups_and_check_cpu_usage(self, group_count=2000):
+        OnosCtrl(self.igmp_app).activate()
+        groups = self.generate_random_multicast_ip_addresses(count = group_count)
+        sources = self.generate_random_unicast_ip_addresses(count = group_count)
+        self.onos_ssm_table_load(groups,src_list=sources,flag=True)
+        for index in range(group_count):
+            self.send_igmp_join(groups = [groups[index]], src_list = [sources[index]],record_type = IGMP_V3_GR_TYPE_INCLUDE,
+                                         iface = self.V_INF1)
+            status = self.verify_igmp_data_traffic(groups[index],intf=self.V_INF1,source=sources[index])
+            assert_equal(status, True)
+            log_test.info('data received for group %s from source %s - %d'%(groups[index],sources[index],index))
+            if index % 50 == 0:
+                cpu_usage = self.get_system_cpu_usage()
+                log.info('CPU usage is %s for multicast group entries %s'%(cpu_usage,index+1))
+
+    def test_scale_igmp_joins_for_2000_multicast_groups_and_check_cpu_usage_after_app_deactivation_and_activation(self,group_count=500):
+        OnosCtrl(self.igmp_app).activate()
+        groups = self.generate_random_multicast_ip_addresses(count = group_count)
+        sources = self.generate_random_unicast_ip_addresses(count = group_count)
+        self.onos_ssm_table_load(groups,src_list=sources,flag=True)
+        for index in range(group_count):
+            self.send_igmp_join(groups = [groups[index]], src_list = [sources[index]],record_type = IGMP_V3_GR_TYPE_INCLUDE,
+                                         iface = self.V_INF1)
+            status = self.verify_igmp_data_traffic(groups[index],intf=self.V_INF1,source=sources[index])
+            assert_equal(status, True)
+            log_test.info('data received for group %s from source %s - %d'%(groups[index],sources[index],index))
+            if index % 50 == 0:
+                cpu_usage = self.get_system_cpu_usage()
+                log.info('CPU usage is %s for multicast group entries %s'%(cpu_usage,index+1))
+        OnosCtrl(self.igmp_app).deactivate()
+        time.sleep(1)
+        cpu_usage = self.get_system_cpu_usage()
+        log.info('CPU usage is %s for multicast group entries %s after igmp app deactivated'%(cpu_usage,index+1))
+