github.com/osrg/gobgp@v2.0.0+incompatible/test/scenario_test/bgp_router_test.py (about) 1 # Copyright (C) 2015 Nippon Telegraph and Telephone Corporation. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 # implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 from __future__ import absolute_import 17 18 import json 19 import sys 20 import time 21 import unittest 22 23 from fabric.api import local 24 import nose 25 26 from lib.noseplugin import OptionParser, parser_option 27 28 from lib import base 29 from lib.base import ( 30 BGP_FSM_IDLE, 31 BGP_FSM_ACTIVE, 32 BGP_FSM_ESTABLISHED, 33 BGP_ATTR_TYPE_MULTI_EXIT_DISC, 34 BGP_ATTR_TYPE_LOCAL_PREF, 35 wait_for_completion, 36 assert_several_times, 37 ) 38 from lib.gobgp import ( 39 GoBGPContainer, 40 extract_path_attribute, 41 ) 42 from lib.quagga import QuaggaBGPContainer 43 from lib.exabgp import ExaBGPContainer 44 45 46 class GoBGPTestBase(unittest.TestCase): 47 48 @classmethod 49 def setUpClass(cls): 50 gobgp_ctn_image_name = parser_option.gobgp_image 51 base.TEST_PREFIX = parser_option.test_prefix 52 53 g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1', 54 ctn_image_name=gobgp_ctn_image_name, 55 log_level=parser_option.gobgp_log_level) 56 q1 = QuaggaBGPContainer(name='q1', asn=65001, router_id='192.168.0.2') 57 q2 = QuaggaBGPContainer(name='q2', asn=65002, router_id='192.168.0.3') 58 q3 = QuaggaBGPContainer(name='q3', asn=65003, router_id='192.168.0.4') 59 60 qs = [q1, q2, q3] 61 ctns = [g1, q1, q2, q3] 62 63 initial_wait_time = max(ctn.run() for ctn in ctns) 64 time.sleep(initial_wait_time) 65 66 for q in qs: 67 g1.add_peer(q, passwd='passwd') 68 q.add_peer(g1, passwd='passwd', passive=True) 69 70 # advertise a route from q1, q2, q3 71 for idx, q in enumerate(qs): 72 route = '10.0.{0}.0/24'.format(idx + 1) 73 q.add_route(route) 74 75 cls.gobgp = g1 76 cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3} 77 78 # test each neighbor state is turned establish 79 def test_01_neighbor_established(self): 80 for q in self.quaggas.itervalues(): 81 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q) 82 83 def test_02_check_gobgp_global_rib(self): 84 for q in self.quaggas.itervalues(): 85 # paths expected to exist in gobgp's global rib 86 routes = q.routes.keys() 87 timeout = 120 88 interval = 1 89 count = 0 90 91 while True: 92 # gobgp's global rib 93 state = self.gobgp.get_neighbor_state(q) 94 self.assertEqual(state, BGP_FSM_ESTABLISHED) 95 global_rib = [p['prefix'] for p in self.gobgp.get_global_rib()] 96 97 for p in global_rib: 98 if p in routes: 99 routes.remove(p) 100 101 if len(routes) == 0: 102 break 103 104 time.sleep(interval) 105 count += interval 106 if count >= timeout: 107 raise Exception('timeout') 108 109 # check gobgp properly add it's own asn to aspath 110 def test_03_check_gobgp_adj_out_rib(self): 111 for q in self.quaggas.itervalues(): 112 for path in self.gobgp.get_adj_rib_out(q): 113 asns = path['aspath'] 114 self.assertTrue(self.gobgp.asn in asns) 115 116 # check routes are properly advertised to all BGP speaker 117 def test_04_check_quagga_global_rib(self): 118 interval = 1 119 timeout = int(120 / interval) 120 for q in self.quaggas.itervalues(): 121 done = False 122 for _ in range(timeout): 123 if done: 124 break 125 global_rib = q.get_global_rib() 126 global_rib = [p['prefix'] for p in global_rib] 127 if len(global_rib) < len(self.quaggas): 128 time.sleep(interval) 129 continue 130 131 self.assertEqual(len(global_rib), len(self.quaggas)) 132 133 for c in self.quaggas.itervalues(): 134 for r in c.routes: 135 self.assertTrue(r in global_rib) 136 done = True 137 if done: 138 continue 139 # should not reach here 140 raise AssertionError 141 142 def test_05_add_quagga(self): 143 q4 = QuaggaBGPContainer(name='q4', asn=65004, router_id='192.168.0.5') 144 self.quaggas['q4'] = q4 145 initial_wait_time = q4.run() 146 time.sleep(initial_wait_time) 147 148 self.gobgp.add_peer(q4) 149 q4.add_peer(self.gobgp) 150 151 q4.add_route('10.0.4.0/24') 152 153 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q4) 154 155 def test_06_check_global_rib(self): 156 self.test_02_check_gobgp_global_rib() 157 self.test_04_check_quagga_global_rib() 158 159 def test_07_stop_one_quagga(self): 160 g1 = self.gobgp 161 q4 = self.quaggas['q4'] 162 q4.stop() 163 self.gobgp.wait_for(expected_state=BGP_FSM_ACTIVE, peer=q4) 164 165 g1.del_peer(q4) 166 del self.quaggas['q4'] 167 168 # check gobgp properly send withdrawal message with q4's route 169 def test_08_check_global_rib(self): 170 self.test_02_check_gobgp_global_rib() 171 self.test_04_check_quagga_global_rib() 172 173 def test_09_add_distant_relative(self): 174 q1 = self.quaggas['q1'] 175 q2 = self.quaggas['q2'] 176 q3 = self.quaggas['q3'] 177 q5 = QuaggaBGPContainer(name='q5', asn=65005, router_id='192.168.0.6') 178 179 initial_wait_time = q5.run() 180 time.sleep(initial_wait_time) 181 182 for q in [q2, q3]: 183 q5.add_peer(q) 184 q.add_peer(q5) 185 186 med200 = {'name': 'med200', 187 'type': 'permit', 188 'match': '0.0.0.0/0', 189 'med': 200} 190 q2.add_policy(med200, self.gobgp, 'out') 191 med100 = {'name': 'med100', 192 'type': 'permit', 193 'match': '0.0.0.0/0', 194 'med': 100} 195 q3.add_policy(med100, self.gobgp, 'out') 196 197 q5.add_route('10.0.6.0/24') 198 199 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q2) 200 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q3) 201 q2.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q5) 202 q3.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q5) 203 204 timeout = 120 205 interval = 1 206 count = 0 207 while True: 208 paths = self.gobgp.get_adj_rib_out(q1, '10.0.6.0/24') 209 if len(paths) > 0: 210 path = paths[0] 211 print "{0}'s nexthop is {1}".format(path['nlri']['prefix'], 212 path['nexthop']) 213 n_addrs = [i[1].split('/')[0] for i in self.gobgp.ip_addrs] 214 if path['nexthop'] in n_addrs: 215 break 216 217 time.sleep(interval) 218 count += interval 219 if count >= timeout: 220 raise Exception('timeout') 221 222 def test_10_originate_path(self): 223 self.gobgp.add_route('10.10.0.0/24') 224 dst = self.gobgp.get_global_rib('10.10.0.0/24') 225 self.assertEqual(len(dst), 1) 226 self.assertEqual(len(dst[0]['paths']), 1) 227 path = dst[0]['paths'][0] 228 self.assertEqual(path['nexthop'], '0.0.0.0') 229 self.assertEqual(len(path['aspath']), 0) 230 231 def test_11_check_adj_rib_out(self): 232 for q in self.quaggas.itervalues(): 233 paths = self.gobgp.get_adj_rib_out(q, '10.10.0.0/24') 234 self.assertEqual(len(paths), 1) 235 path = paths[0] 236 peer_info = self.gobgp.peers[q] 237 local_addr = peer_info['local_addr'].split('/')[0] 238 self.assertEqual(path['nexthop'], local_addr) 239 self.assertEqual(path['aspath'], [self.gobgp.asn]) 240 241 def test_12_disable_peer(self): 242 q1 = self.quaggas['q1'] 243 self.gobgp.disable_peer(q1) 244 self.gobgp.wait_for(expected_state=BGP_FSM_IDLE, peer=q1) 245 246 time.sleep(3) 247 248 for route in q1.routes.iterkeys(): 249 dst = self.gobgp.get_global_rib(route) 250 self.assertEqual(len(dst), 0) 251 252 for q in self.quaggas.itervalues(): 253 if q is q1: 254 continue 255 paths = self.gobgp.get_adj_rib_out(q, route) 256 self.assertEqual(len(paths), 0) 257 258 def test_13_enable_peer(self): 259 q1 = self.quaggas['q1'] 260 self.gobgp.enable_peer(q1) 261 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q1) 262 263 def test_14_check_adj_rib_out(self): 264 self.test_11_check_adj_rib_out() 265 266 def test_15_check_active_connection(self): 267 g1 = self.gobgp 268 g2 = GoBGPContainer(name='g2', asn=65000, router_id='192.168.0.7', 269 ctn_image_name=self.gobgp.image, 270 log_level=parser_option.gobgp_log_level) 271 time.sleep(g2.run()) 272 self.quaggas['g2'] = g2 273 g2.add_peer(g1, passive=True) 274 g1.add_peer(g2) 275 g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=g2) 276 277 def test_16_check_local_pref_and_med_handling(self): 278 g1 = self.gobgp 279 g1.add_route('10.20.0.0/24', local_pref=1000, med=2000) 280 # iBGP peer 281 g2 = self.quaggas['g2'] 282 paths = g2.get_global_rib('10.20.0.0/24') 283 self.assertEqual(len(paths), 1) 284 self.assertEqual(len(paths[0]['paths']), 1) 285 path = paths[0]['paths'][0] 286 local_pref = extract_path_attribute(path, BGP_ATTR_TYPE_LOCAL_PREF) 287 self.assertEqual(local_pref['value'], 1000) 288 med = extract_path_attribute(path, BGP_ATTR_TYPE_MULTI_EXIT_DISC) 289 self.assertEqual(med['metric'], 2000) 290 291 # eBGP peer 292 q1 = self.quaggas['q1'] 293 paths = q1.get_global_rib('10.20.0.0/24') 294 self.assertEqual(len(paths), 1) 295 path = paths[0] 296 local_pref = extract_path_attribute(path, BGP_ATTR_TYPE_LOCAL_PREF) 297 # local_pref's default value is 100 298 self.assertEqual(local_pref['value'], 100) 299 med = extract_path_attribute(path, BGP_ATTR_TYPE_MULTI_EXIT_DISC) 300 self.assertEqual(med['metric'], 2000) 301 302 def test_17_check_shutdown(self): 303 g1 = self.gobgp 304 q1 = self.quaggas['q1'] 305 q2 = self.quaggas['q2'] 306 q3 = self.quaggas['q3'] 307 308 q2.add_route('20.0.0.0/24') 309 q3.add_route('20.0.0.0/24') 310 311 self.test_01_neighbor_established() 312 313 self.test_02_check_gobgp_global_rib() 314 315 paths = q1.get_global_rib('20.0.0.0/24') 316 self.assertEqual(len(paths), 1) 317 n_addrs = [i[1].split('/')[0] for i in self.gobgp.ip_addrs] 318 self.assertIn(paths[0]['nexthop'], n_addrs) 319 320 q3.stop() 321 322 self.gobgp.wait_for(expected_state=BGP_FSM_ACTIVE, peer=q3) 323 324 def f(): 325 paths = q1.get_global_rib('20.0.0.0/24') 326 self.assertEqual(len(paths), 1) 327 self.assertIn(paths[0]['nexthop'], n_addrs) 328 329 assert_several_times(f) 330 331 g1.del_peer(q3) 332 del self.quaggas['q3'] 333 334 def test_18_check_withdrawal(self): 335 g1 = self.gobgp 336 q1 = self.quaggas['q1'] 337 q2 = self.quaggas['q2'] 338 339 g1.add_route('30.0.0.0/24') 340 q1.add_route('30.0.0.0/24') 341 342 self.test_01_neighbor_established() 343 344 self.test_02_check_gobgp_global_rib() 345 346 paths = g1.get_adj_rib_out(q1, '30.0.0.0/24') 347 self.assertEqual(len(paths), 1) 348 self.assertNotIn('source-id', paths[0]) 349 paths = g1.get_adj_rib_out(q2, '30.0.0.0/24') 350 self.assertEqual(len(paths), 1) 351 self.assertNotIn('source-id', paths[0]) 352 353 g1.local('gobgp global rib del 30.0.0.0/24') 354 355 def f(): 356 paths = g1.get_adj_rib_out(q1, '30.0.0.0/24') 357 self.assertEqual(len(paths), 0) 358 paths = g1.get_adj_rib_out(q2, '30.0.0.0/24') 359 self.assertEqual(len(paths), 1) 360 self.assertEqual(paths[0]['source-id'], '192.168.0.2') 361 362 assert_several_times(f) 363 364 def test_19_check_grpc_add_neighbor(self): 365 g1 = self.gobgp 366 e1 = ExaBGPContainer(name='e1', asn=65000, router_id='192.168.0.7') 367 time.sleep(e1.run()) 368 e1.add_peer(g1) 369 self.quaggas['e1'] = e1 370 n = e1.peers[g1]['local_addr'].split('/')[0] 371 g1.local('gobgp n add {0} as 65000'.format(n)) 372 g1.add_peer(e1, reload_config=False) 373 374 g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=e1) 375 376 def test_20_check_grpc_del_neighbor(self): 377 g1 = self.gobgp 378 e1 = self.quaggas['e1'] 379 n = e1.peers[g1]['local_addr'].split('/')[0] 380 g1.local('gobgp n del {0}'.format(n)) 381 g1.del_peer(e1, reload_config=False) 382 383 def test_21_check_withdrawal_2(self): 384 g1 = self.gobgp 385 g2 = self.quaggas['g2'] 386 387 prefix = '40.10.0.0/24' 388 g1.add_route(prefix) 389 wait_for_completion(lambda: len(g1.get_global_rib(prefix)) == 1) 390 wait_for_completion(lambda: len(g2.get_global_rib(prefix)) == 1) 391 392 r = g2.local('gobgp monitor global rib -j', stream=True, tty=False) 393 394 g1.local('gobgp global rib del 40.10.0.0/24') 395 del g1.routes[prefix] 396 397 wait_for_completion(lambda: len(g1.get_global_rib(prefix)) == 0) 398 wait_for_completion(lambda: len(g2.get_global_rib(prefix)) == 0) 399 400 ret = json.loads(r.next()) 401 self.assertEqual(ret[0]['nlri']['prefix'], prefix) 402 self.assertTrue('withdrawal' in ret[0]) 403 404 def test_22_check_cli_sorted(self): 405 g1 = self.gobgp 406 cnt = 0 407 408 def next_prefix(): 409 for i in range(100, 105): 410 for j in range(100, 105): 411 yield '{0}.{1}.0.0/24'.format(i, j) 412 413 for p in next_prefix(): 414 g1.local('gobgp global rib add {0}'.format(p)) 415 cnt += 1 416 417 cnt2 = 0 418 g = next_prefix() 419 n = g.next() 420 for path in g1.local("gobgp global rib", capture=True).split('\n')[1:]: 421 if [elem for elem in path.split(' ') if elem != ''][1] == n: 422 try: 423 cnt2 += 1 424 n = g.next() 425 except StopIteration: 426 break 427 428 self.assertEqual(cnt, cnt2) 429 430 def test_23_check_withdrawal3(self): 431 gobgp_ctn_image_name = parser_option.gobgp_image 432 g1 = self.gobgp 433 g3 = GoBGPContainer(name='g3', asn=65006, router_id='192.168.0.8', 434 ctn_image_name=gobgp_ctn_image_name, 435 log_level=parser_option.gobgp_log_level) 436 g4 = GoBGPContainer(name='g4', asn=65007, router_id='192.168.0.9', 437 ctn_image_name=gobgp_ctn_image_name, 438 log_level=parser_option.gobgp_log_level) 439 440 initial_wait_time = max(ctn.run() for ctn in [g3, g4]) 441 time.sleep(initial_wait_time) 442 443 self.quaggas = {'g3': g3, 'g4': g4} 444 445 g3.local('gobgp global rib add 50.0.0.0/24') 446 447 g1.add_peer(g3, passive=True) 448 g3.add_peer(g1) 449 g1.add_peer(g4, passive=True) 450 g4.add_peer(g1) 451 452 self.test_01_neighbor_established() 453 454 self.test_02_check_gobgp_global_rib() 455 456 g4.local('gobgp global rib add 50.0.0.0/24 med 10') 457 458 paths = g1.get_adj_rib_out(g3, '50.0.0.0/24') 459 self.assertEqual(len(paths), 0) 460 paths = g1.get_adj_rib_out(g4, '50.0.0.0/24') 461 self.assertEqual(len(paths), 1) 462 self.assertEqual(paths[0]['source-id'], '192.168.0.8') 463 464 g3.local('gobgp global rib del 50.0.0.0/24') 465 466 paths = g1.get_adj_rib_out(g3, '50.0.0.0/24') 467 self.assertEqual(len(paths), 1) 468 self.assertEqual(paths[0]['source-id'], '192.168.0.9') 469 paths = g1.get_adj_rib_out(g4, '50.0.0.0/24') 470 self.assertEqual(len(paths), 0) 471 472 473 if __name__ == '__main__': 474 output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True) 475 if int(output) is not 0: 476 print "docker not found" 477 sys.exit(1) 478 479 nose.main(argv=sys.argv, addplugins=[OptionParser()], 480 defaultTest=sys.argv[0])