Added provision in setup argument to run external dhcp server in cord-tester container and testing it from someother node. Implemented new dhcpl2relay test cases also.

Change-Id: I1e469864f24b1b5b8a1555f037dcd8c3ebdd44e2
diff --git a/src/test/setup/cord-test.py b/src/test/setup/cord-test.py
index 66dd299..cef7e57 100755
--- a/src/test/setup/cord-test.py
+++ b/src/test/setup/cord-test.py
@@ -60,6 +60,25 @@
     'cordSubscriber', 'vrouter', 'flows', 'proxyarp', 'acl', 'xos', 'fabric',
     'cbench', 'cluster', 'netCondition', 'cordvtn', 'iperf', 'mini', 'vsg')
 
+    dhcp_data_dir = os.path.join(tester_base, '..', 'setup')
+    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"'),
+                   ]
+    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;
+}
+'''), ]
+    host_ip_map = {}
+    relay_interfaces_last = ()
+    interface_to_mac_map = {}
+    configs = {}
+
+
     def __init__(self, tests, instance = 0, num_instances = 1, ctlr_ip = None,
                  name = '', image = IMAGE, prefix = '', tag = 'candidate',
                  env = None, rm = False, update = False, network = None):
@@ -215,6 +234,148 @@
 
         self.switch_started = True
 
+    def setup_dhcpd(self,  manifest, boot_delay = 5):
+        if manifest.start_switch:
+           switch_starts = True
+        else:
+           return False
+        setup_for_relay = self.dhcp_relay_setup()
+        dhcp_start_status = self.dhcpd_start()
+        if setup_for_relay and dhcp_start_status:
+           return True
+        else:
+           return False
+
+    def dhcp_relay_setup(self):
+        did = OnosCtrl.get_device_id()
+        self.relay_device_id = did
+        #self.olt = OltConfig(olt_conf_file = self.olt_conf_file)
+        #self.port_map, _ = self.olt.olt_port_map()
+        if self.port_map:
+            ##Per subscriber, we use 1 relay port
+            try:
+                relay_port = self.port_map[self.port_map['relay_ports'][0]]
+            except:
+                relay_port = self.port_map['uplink']
+            self.relay_interface_port = relay_port
+            self.relay_interfaces = (self.port_map[self.relay_interface_port],)
+        else:
+             print 'Setup dhcpd we must have port_map'
+             return False
+        if self.port_map:
+            ##generate a ip/mac client virtual interface config for onos
+            interface_list = []
+            for port in self.port_map['ports']:
+                port_num = self.port_map[port]
+                if port_num == self.port_map['uplink']:
+                    continue
+                ip = self.get_host_ip(port_num)
+                mac = self.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 = self.get_host_ip(interface_list[0][0])
+            relay_mac = self.get_mac(self.port_map[self.relay_interface_port])
+            interface_list.append((self.relay_interface_port, relay_ip, relay_mac))
+            self.onos_interface_load(interface_list)
+        return True
+
+    def onos_load_config(cls, 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(3)
+
+    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)
+        cls.configs['interface_config'] = interface_dict
+
+    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.100.{}'.format(port)
+        return cls.host_ip_map[port]
+
+    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 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)
+
+    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
+            cmd = 'ifconfig {} {}'.format(intf, ip)
+            status = cls.execute(cmd, shell = True)
+
+        intf_str = ','.join(intf_list)
+        dhcpd_cmd = '/usr/sbin/dhcpd -4 --no-pid -cf {0} -lf {1} {2}'.format('/root/test/src/test/setup/dhcpd.conf','/root/test/src/test/setup/dhcpd.leases', intf_str)
+        print('Starting DHCPD server with command: %s' %dhcpd_cmd)
+        status = cls.execute(dhcpd_cmd, shell = True)
+        if status > 255:
+           status = 1
+        else:
+           return False
+        time.sleep(3)
+        cls.relay_interfaces_last = cls.relay_interfaces
+        cls.relay_interfaces = intf_list
+        #cls.onos_dhcp_relay_load(*intf_info[0])
+        return True
+
     def setup_vcpes(self, port_num = 0):
         res = 0
         for vcpe in self.vcpes:
@@ -1070,6 +1231,8 @@
             test_cnt.start_switch(test_manifest)
         if test_cnt.olt:
             test_cnt.setup_intfs(port_num = 0)
+        if test_manifest.setup_dhcpd and test_manifest.start_switch:
+           test_cnt.setup_dhcpd(test_manifest)
         print('Test container %s started and provisioned to run tests using nosetests' %(test_cnt.name))
 
     #Finally start the test server and daemonize
@@ -1389,6 +1552,7 @@
                               choices=['DEBUG','TRACE','ERROR','WARN','INFO'],
                               help='Specify the log level for the test cases')
     parser_setup.add_argument('-s', '--start-switch', action='store_true', help='Start OVS when running under OLT config')
+    parser_setup.add_argument('-dh', '--setup-dhcpd', action='store_true', help='Start dhcpd Server in external container may be in cord-tester')
     parser_setup.add_argument('-onos-cord', '--onos-cord', default='', type=str,
                               help='Specify config location for ONOS cord when running on podd')
     parser_setup.add_argument('-service-profile', '--service-profile', default='', type=str,