blob: 945539f396db68b97f39a38ddef5bb97635a3405 [file] [log] [blame]
Chetan Gaonker3533faa2016-04-25 17:50:14 -07001import unittest
2from nose.tools import *
3from scapy.all import *
4from OnosCtrl import OnosCtrl
5from OltConfig import OltConfig
6from OnosFlowCtrl import OnosFlowCtrl, get_mac
7from onosclidriver import OnosCliDriver
8from CordContainer import Container, Onos
9from CordTestServer import cord_test_onos_restart
10from portmaps import g_subscriber_port_map
11import threading
12import time
13import os
14import json
15log.setLevel('INFO')
16
17class Quagga(Container):
18 quagga_config = '/root/config'
19 def __init__(self, name = 'cord-quagga', image = 'cord-test/quagga', tag = 'latest'):
20 super(Quagga, self).__init__(name, image, tag = tag)
21 if not self.exists():
22 raise Exception('Quagga container was not started by cord-test')
23
24class vrouter_exchange(unittest.TestCase):
25
26 apps = ('org.onosproject.vrouter', 'org.onosproject.fwd')
27 device_id = 'of:' + get_mac('ovsbr0')
28 vrouter_device_dict = { "devices" : {
29 "{}".format(device_id) : {
30 "basic" : {
31 "driver" : "softrouter"
32 }
33 }
34 },
35 }
36 zebra_conf = '''
37password zebra
38log stdout
39service advanced-vty
40!
41debug zebra rib
42debug zebra kernel
43debug zebra fpm
44!
45!interface eth1
46! ip address 10.10.0.3/16
47line vty
48 exec-timeout 0 0
49'''
50 test_path = os.path.dirname(os.path.realpath(__file__))
51 quagga_config_path = os.path.join(test_path, '..', 'setup/quagga-config')
52 onos_config_path = os.path.join(test_path, '..', 'setup/onos-config')
53 GATEWAY = '172.17.0.50'
54 MAX_PORTS = 100
55
56 @classmethod
57 def setUpClass(cls):
58 ''' Activate the vrouter apps'''
59 cls.olt = OltConfig()
60 cls.port_map = cls.olt.olt_port_map()
61 if not cls.port_map:
62 cls.port_map = g_subscriber_port_map
63 cls.vrouter_host_load(host = cls.GATEWAY)
64 time.sleep(3)
65
66 @classmethod
67 def tearDownClass(cls):
68 '''Deactivate the vrouter apps'''
69 cls.vrouter_host_unload()
70
71 def cliEnter(self):
72 retries = 0
73 while retries < 3:
74 self.cli = OnosCliDriver(connect = True)
75 if self.cli.handle:
76 break
77 else:
78 retries += 1
79 time.sleep(2)
80
81 def cliExit(self):
82 self.cli.disconnect()
83
84 @classmethod
85 def onos_load_config(cls, config):
86 status, code = OnosCtrl.config(config)
87 if status is False:
88 log.info('JSON request returned status %d' %code)
89 assert_equal(status, True)
90
91 @classmethod
92 def vrouter_config_get(cls, networks = 4):
93 vrouter_configs = cls.generate_vrouter_conf(networks = networks)
94 return vrouter_configs
95 ##ONOS router does not support dynamic reconfigurations
96 #for config in vrouter_configs:
97 # cls.onos_load_config(config)
98 # time.sleep(5)
99
100 @classmethod
101 def vrouter_host_load(cls, host=GATEWAY, iface = 'veth0'):
102 config_cmds = ( 'ifconfig {0} {1}'.format(iface, host),
103 'arping -I {0} {1} -c 2'.format(iface, host),
104 )
105 for cmd in config_cmds:
106 os.system(cmd)
107
108 @classmethod
109 def vrouter_host_unload(cls, iface='veth0'):
110 config_cmds = ('ifconfig {} 0'.format(iface), )
111 for cmd in config_cmds:
112 os.system(cmd)
113
114 @classmethod
115 def start_onos(cls, network_cfg = None):
116 if type(network_cfg) is tuple:
117 res = []
118 for v in network_cfg:
119 res += v.items()
120 config = dict(res)
121 else:
122 config = network_cfg
123 log.info('Restarting ONOS with new network configuration')
124 cfg = json.dumps(config)
125 with open('{}/network-cfg.json'.format(cls.onos_config_path), 'w') as f:
126 f.write(cfg)
127
128 return cord_test_onos_restart()
129
130 @classmethod
131 def start_quagga(cls, stop = True, networks = 4, gateway = GATEWAY):
132 quagga = Quagga()
133 log.info('Starting Quagga on container %s with %d networks' %(quagga.name, networks))
134 if stop is True:
135 quagga.execute('{}/stop.sh'.format(Quagga.quagga_config))
136 config = cls.generate_conf(networks = networks, gateway = gateway)
137 with open('{}/testrib_gen.conf'.format(cls.quagga_config_path), 'w') as f:
138 f.write(config)
139 quagga.execute('{0}/start.sh {1}/testrib_gen.conf'.format(Quagga.quagga_config, Quagga.quagga_config))
140 time.sleep(10)
141
142 @classmethod
143 def generate_vrouter_conf(cls, networks = 4):
144 num = 0
145 start_network = ( 11 << 24) | ( 0 << 16) | ( 0 << 8) | 0
146 end_network = ( 200 << 24 ) | ( 0 << 16) | (0 << 8) | 0
147 ports_dict = { 'ports' : {} }
148 interface_list = []
149 for n in xrange(start_network, end_network):
150 if n & 255 == 0:
151 port_map = ports_dict['ports']
152 port = num + 1 if num < cls.MAX_PORTS - 1 else cls.MAX_PORTS - 1
153 device_port_key = '{0}/{1}'.format(cls.device_id, port)
154 try:
155 interfaces = port_map[device_port_key]['interfaces']
156 except:
157 port_map[device_port_key] = { 'interfaces' : [] }
158 interfaces = port_map[device_port_key]['interfaces']
159
160 ips = '%d.%d.%d.2/24'%( (n >> 24) & 0xff, ( ( n >> 16) & 0xff ), ( (n >> 8 ) & 0xff ) )
161 if num < cls.MAX_PORTS - 1:
162 interface_dict = { 'name' : 'b1-{}'.format(port), 'ips': [ips], 'mac' : '00:00:00:00:00:01' }
163 interfaces.append(interface_dict)
164 interface_list.append(interface_dict['name'])
165 else:
166 interfaces[0]['ips'].append(ips)
167 num += 1
168 if num == networks:
169 break
170 quagga_dict = { 'apps': { 'org.onosproject.router' : { 'router' : {} } } }
171 quagga_router_dict = quagga_dict['apps']['org.onosproject.router']['router']
172 quagga_router_dict['ospfEnabled'] = True
173 quagga_router_dict['interfaces'] = interface_list
174 quagga_router_dict['controlPlaneConnectPoint'] = '{0}/{1}'.format(cls.device_id,
175 networks + 1 if networks < cls.MAX_PORTS else cls.MAX_PORTS )
176 return (cls.vrouter_device_dict, ports_dict, quagga_dict)
177
178 @classmethod
179 def generate_conf(cls, networks = 4, gateway = GATEWAY):
180 num = 0
181 start_network = ( 11 << 24) | ( 0 << 16) | ( 0 << 8) | 0
182 end_network = ( 200 << 24 ) | ( 0 << 16) | (0 << 8) | 0
183 net_list = []
184 for n in xrange(start_network, end_network):
185 if n & 255 == 0:
186 net = '%d.%d.%d.0'%( (n >> 24) & 0xff, ( ( n >> 16) & 0xff ), ( (n >> 8 ) & 0xff ) )
187 net_route = 'ip route {0}/24 {1}'.format(net, gateway)
188 net_list.append(net_route)
189 num += 1
190 if num == networks:
191 break
192 zebra_routes = '\n'.join(net_list)
193 log.info('Zebra routes: \n:%s\n' %cls.zebra_conf + zebra_routes)
194 return cls.zebra_conf + zebra_routes
195
196 @classmethod
197 def vrouter_activate(cls, deactivate = False):
198 app = 'org.onosproject.vrouter'
199 onos_ctrl = OnosCtrl(app)
200 if deactivate is True:
201 onos_ctrl.deactivate()
202 else:
203 onos_ctrl.activate()
204 time.sleep(3)
205
206 @classmethod
207 def vrouter_configure(cls, networks = 4):
208 ##Deactivate vrouter
209 vrouter_configs = cls.vrouter_config_get(networks = networks)
210 #cls.start_onos(network_cfg = vrouter_configs)
211 ##Start quagga with 4 networks
212 #cls.start_quagga(networks = networks, stop = True, gateway = cls.GATEWAY)
213 return vrouter_configs
214
215 def vrouter_port_send_recv(self, ingress, egress, dst_mac, dst_ip):
216 src_mac = '00:00:00:00:00:02'
217 src_ip = '172.17.0.100'
218 self.success = False
219 def recv_task():
220 def recv_cb(pkt):
221 log.info('Pkt seen with ingress ip %s, egress ip %s' %(pkt[IP].src, pkt[IP].dst))
222 self.success = True
223 sniff(count=2, timeout=5,
224 lfilter = lambda p: IP in p and p[IP].dst == dst_ip and p[IP].src == src_ip,
225 prn = recv_cb, iface = self.port_map[egress])
226
227 t = threading.Thread(target = recv_task)
228 t.start()
229 L2 = Ether(src = src_mac, dst = dst_mac)
230 L3 = IP(src = src_ip, dst = dst_ip)
231 pkt = L2/L3
232 log.info('Sending a packet with dst ip %s, dst mac %s on port %s to verify if flows are correct' %
233 (dst_ip, dst_mac, self.port_map[ingress]))
234 sendp(pkt, count=50, iface = self.port_map[ingress])
235 t.join()
236 assert_equal(self.success, True)
237
238 def vrouter_traffic_verify(self, ports_dict, egress_dict):
239 egress = int(egress_dict['apps']['org.onosproject.router']['router']['controlPlaneConnectPoint'].split('/')[1])
240 for dev in ports_dict['ports'].keys():
241 for intf in ports_dict['ports'][dev]['interfaces']:
242 for ip in intf['ips']:
243 dst_ip = ip.split('/')[0]
244 dst_mac = intf['mac']
245 port = intf['name']
246 ingress = int(port.split('-')[1])
247 ##Verify if flows are setup by sending traffic across
248 self.vrouter_port_send_recv(ingress, egress, dst_mac, dst_ip)
249
250 def __vrouter_network_verify(self, networks):
251 _, ports_map, egress_map = self.vrouter_configure(networks = networks)
252 self.cliEnter()
253 ##Now verify
254 hosts = json.loads(self.cli.hosts(jsonFormat = True))
255 log.info('Discovered hosts: %s' %hosts)
256 routes = json.loads(self.cli.routes(jsonFormat = True))
257 log.info('Routes: %s' %routes)
258 #assert_equal(len(routes['routes4']), networks)
259 flows = json.loads(self.cli.flows(jsonFormat = True))
260 flows = filter(lambda f: f['flows'], flows)
261 #log.info('Flows: %s' %flows)
262 assert_not_equal(len(flows), 0)
263 self.vrouter_traffic_verify(ports_map, egress_map)
264 self.cliExit()
265 return True
266
267 def test_vrouter_1(self):
268 '''Test vrouter with 5 routes'''
269 res = self.__vrouter_network_verify(5)
270 assert_equal(res, True)
271
272 def test_vrouter_2(self):
273 '''Test vrouter with 20 routes'''
274 res = self.__vrouter_network_verify(50)
275 assert_equal(res, True)
276
277 def test_vrouter_3(self):
278 '''Test vrouter with 100 routes'''
279 res = self.__vrouter_network_verify(100)
280 assert_equal(res, True)
281
282 def test_vrouter_4(self):
283 '''Test vrouter with 200 routes'''
284 res = self.__vrouter_network_verify(300)
285 assert_equal(res, True)