Implement changes to cord-tester framework for vrouter configuration.
And add vrouter tests to verify routes.
diff --git a/src/test/vrouter/vrouterTest.py b/src/test/vrouter/vrouterTest.py
new file mode 100644
index 0000000..945539f
--- /dev/null
+++ b/src/test/vrouter/vrouterTest.py
@@ -0,0 +1,285 @@
+import unittest
+from nose.tools import *
+from scapy.all import *
+from OnosCtrl import OnosCtrl
+from OltConfig import OltConfig
+from OnosFlowCtrl import OnosFlowCtrl, get_mac
+from onosclidriver import OnosCliDriver
+from CordContainer import Container, Onos
+from CordTestServer import cord_test_onos_restart
+from portmaps import g_subscriber_port_map
+import threading
+import time
+import os
+import json
+log.setLevel('INFO')
+
+class Quagga(Container):
+    quagga_config = '/root/config'
+    def __init__(self, name = 'cord-quagga', image = 'cord-test/quagga', tag = 'latest'):
+        super(Quagga, self).__init__(name, image, tag = tag)
+        if not self.exists():
+            raise Exception('Quagga container was not started by cord-test')
+        
+class vrouter_exchange(unittest.TestCase):
+
+    apps = ('org.onosproject.vrouter', 'org.onosproject.fwd')
+    device_id = 'of:' + get_mac('ovsbr0')
+    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 = '172.17.0.50'
+    MAX_PORTS = 100
+
+    @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
+        cls.vrouter_host_load(host = cls.GATEWAY)
+        time.sleep(3)
+        
+    @classmethod
+    def tearDownClass(cls):
+        '''Deactivate the vrouter apps'''
+        cls.vrouter_host_unload()
+
+    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):
+        vrouter_configs = cls.generate_vrouter_conf(networks = networks)
+        return vrouter_configs
+        ##ONOS router does not support dynamic reconfigurations
+        #for config in vrouter_configs:
+        #    cls.onos_load_config(config)
+        #    time.sleep(5)
+
+    @classmethod
+    def vrouter_host_load(cls, host=GATEWAY, iface = 'veth0'):
+        config_cmds = ( '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, iface='veth0'):
+        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')
+        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()
+
+    @classmethod
+    def start_quagga(cls, stop = True, networks = 4, gateway = GATEWAY):
+        quagga = Quagga()
+        log.info('Starting Quagga on container %s with %d networks' %(quagga.name, networks))
+        if stop is True:
+            quagga.execute('{}/stop.sh'.format(Quagga.quagga_config))
+        config = cls.generate_conf(networks = networks, gateway = gateway)
+        with open('{}/testrib_gen.conf'.format(cls.quagga_config_path), 'w') as f:
+            f.write(config)
+        quagga.execute('{0}/start.sh {1}/testrib_gen.conf'.format(Quagga.quagga_config, Quagga.quagga_config))
+        time.sleep(10)
+
+    @classmethod
+    def generate_vrouter_conf(cls, networks = 4):
+        num = 0
+        start_network = ( 11 << 24) | ( 0 << 16) | ( 0 << 8) | 0
+        end_network =   ( 200 << 24 ) | ( 0 << 16)  | (0 << 8) | 0
+        ports_dict = { 'ports' : {} }
+        interface_list = []
+        for n in xrange(start_network, end_network):
+            if n & 255 == 0:
+                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']
+                    
+                ips = '%d.%d.%d.2/24'%( (n >> 24) & 0xff, ( ( n >> 16) & 0xff ), ( (n >> 8 ) & 0xff ) )
+                if num < cls.MAX_PORTS - 1:
+                    interface_dict = { 'name' : 'b1-{}'.format(port), 'ips': [ips], 'mac' : '00:00:00:00:00:01' }
+                    interfaces.append(interface_dict)
+                    interface_list.append(interface_dict['name'])
+                else:
+                    interfaces[0]['ips'].append(ips)
+                num += 1
+                if num == networks:
+                    break
+        quagga_dict = { 'apps': { 'org.onosproject.router' : { 'router' : {} } } }
+        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, 
+                                                                          networks + 1 if networks < cls.MAX_PORTS else cls.MAX_PORTS )
+        return (cls.vrouter_device_dict, ports_dict, quagga_dict)
+
+    @classmethod
+    def generate_conf(cls, networks = 4, gateway = GATEWAY):
+        num = 0
+        start_network = ( 11 << 24) | ( 0 << 16) | ( 0 << 8) | 0
+        end_network =   ( 200 << 24 ) | ( 0 << 16)  | (0 << 8) | 0
+        net_list = []
+        for n in xrange(start_network, end_network):
+            if n & 255 == 0:
+                net = '%d.%d.%d.0'%( (n >> 24) & 0xff, ( ( n >> 16) & 0xff ), ( (n >> 8 ) & 0xff ) )
+                net_route = 'ip route {0}/24 {1}'.format(net, gateway)
+                net_list.append(net_route)
+                num += 1
+            if num == networks:
+                break
+        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):
+        ##Deactivate vrouter
+        vrouter_configs = cls.vrouter_config_get(networks = networks)
+        #cls.start_onos(network_cfg = vrouter_configs)
+        ##Start quagga with 4 networks
+        #cls.start_quagga(networks = networks, stop = True, gateway = cls.GATEWAY)
+        return vrouter_configs
+    
+    def vrouter_port_send_recv(self, ingress, egress, dst_mac, dst_ip):
+        src_mac = '00:00:00:00:00:02'
+        src_ip = '172.17.0.100'
+        self.success = False
+        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
+            sniff(count=2, timeout=5, 
+                  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[egress])
+
+        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[ingress]))
+        sendp(pkt, count=50, iface = self.port_map[ingress])
+        t.join()
+        assert_equal(self.success, True)
+
+    def vrouter_traffic_verify(self, ports_dict, egress_dict):
+        egress = int(egress_dict['apps']['org.onosproject.router']['router']['controlPlaneConnectPoint'].split('/')[1])
+        for dev in ports_dict['ports'].keys():
+            for intf in ports_dict['ports'][dev]['interfaces']:
+                for ip in intf['ips']:
+                    dst_ip = ip.split('/')[0]
+                    dst_mac = intf['mac']
+                    port = intf['name']
+                    ingress = int(port.split('-')[1])
+                    ##Verify if flows are setup by sending traffic across
+                    self.vrouter_port_send_recv(ingress, egress, dst_mac, dst_ip)
+
+    def __vrouter_network_verify(self, networks):
+        _, ports_map, egress_map = self.vrouter_configure(networks = networks)
+        self.cliEnter()
+        ##Now verify
+        hosts = json.loads(self.cli.hosts(jsonFormat = True))
+        log.info('Discovered hosts: %s' %hosts)
+        routes = json.loads(self.cli.routes(jsonFormat = True))
+        log.info('Routes: %s' %routes)
+        #assert_equal(len(routes['routes4']), networks)
+        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)
+        self.vrouter_traffic_verify(ports_map, egress_map)
+        self.cliExit()
+        return True
+
+    def test_vrouter_1(self):
+        '''Test vrouter with 5 routes'''
+        res = self.__vrouter_network_verify(5)
+        assert_equal(res, True)
+
+    def test_vrouter_2(self):
+        '''Test vrouter with 20 routes'''
+        res = self.__vrouter_network_verify(50)
+        assert_equal(res, True)
+
+    def test_vrouter_3(self):
+        '''Test vrouter with 100 routes'''
+        res = self.__vrouter_network_verify(100)
+        assert_equal(res, True)
+
+    def test_vrouter_4(self):
+        '''Test vrouter with 200 routes'''
+        res = self.__vrouter_network_verify(300)
+        assert_equal(res, True)