github.com/osrg/gobgp/v3@v3.30.0/test/scenario_test/route_reflector_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 17 import sys 18 import time 19 import unittest 20 21 import collections 22 collections.Callable = collections.abc.Callable 23 24 import nose 25 26 from lib.noseplugin import OptionParser, parser_option 27 28 from lib import base 29 from lib.base import BGP_FSM_ESTABLISHED, local 30 from lib.gobgp import GoBGPContainer 31 from lib.quagga import QuaggaBGPContainer 32 33 34 def wait_for(f, timeout=120): 35 interval = 1 36 count = 0 37 while True: 38 if f(): 39 return 40 41 time.sleep(interval) 42 count += interval 43 if count >= timeout: 44 raise Exception('timeout') 45 46 47 class GoBGPTestBase(unittest.TestCase): 48 def assert_adv_count(self, src, dst, rf, count): 49 self.assertEqual(count, len(src.get_adj_rib_out(dst, rf=rf))) 50 self.assertEqual(count, len(dst.get_adj_rib_in(src, rf=rf))) 51 52 def assert_upd_count(self, src, dst, sent, received): 53 messages = src.get_neighbor(dst)['state']['messages'] 54 self.assertEqual(messages['sent'].get('update', 0), sent) 55 self.assertEqual(messages['received'].get('update', 0), received) 56 57 @classmethod 58 def setUpClass(cls): 59 gobgp_ctn_image_name = parser_option.gobgp_image 60 base.TEST_PREFIX = parser_option.test_prefix 61 62 g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1', 63 ctn_image_name=gobgp_ctn_image_name, 64 log_level=parser_option.gobgp_log_level) 65 q1 = QuaggaBGPContainer(name='q1', asn=65000, router_id='192.168.0.2') 66 q2 = QuaggaBGPContainer(name='q2', asn=65000, router_id='192.168.0.3') 67 q3 = QuaggaBGPContainer(name='q3', asn=65000, router_id='192.168.0.4') 68 q4 = QuaggaBGPContainer(name='q4', asn=65000, router_id='192.168.0.5') 69 70 qs = [q1, q2, q3, q4] 71 ctns = [g1, q1, q2, q3, q4] 72 73 initial_wait_time = max(ctn.run() for ctn in ctns) 74 time.sleep(initial_wait_time) 75 76 # g1 as a route reflector 77 g1.add_peer(q1, is_rr_client=True) 78 q1.add_peer(g1) 79 g1.add_peer(q2, is_rr_client=True) 80 q2.add_peer(g1) 81 g1.add_peer(q3) 82 q3.add_peer(g1) 83 g1.add_peer(q4) 84 q4.add_peer(g1) 85 86 # advertise a route from q1, q2 87 for idx, c in enumerate(qs): 88 route = '10.0.{0}.0/24'.format(idx + 1) 89 c.add_route(route) 90 91 cls.gobgp = g1 92 cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3, 'q4': q4} 93 94 # test each neighbor state is turned establish 95 def test_01_neighbor_established(self): 96 for q in self.quaggas.values(): 97 self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q) 98 99 def test_02_check_gobgp_global_rib(self): 100 for q in self.quaggas.values(): 101 # paths expected to exist in gobgp's global rib 102 def f(): 103 state = self.gobgp.get_neighbor_state(q) 104 self.assertEqual(state, BGP_FSM_ESTABLISHED) 105 106 routes = list(q.routes.keys()) 107 global_rib = [p['prefix'] for p in self.gobgp.get_global_rib()] 108 for p in global_rib: 109 if p in routes: 110 routes.remove(p) 111 112 return len(routes) == 0 113 wait_for(f) 114 115 def test_03_check_gobgp_adj_rib_out(self): 116 for q in self.quaggas.values(): 117 paths = [p['nlri']['prefix'] for p in self.gobgp.get_adj_rib_out(q)] 118 for qq in self.quaggas.values(): 119 if q == qq: 120 continue 121 if self.gobgp.peers[q]['is_rr_client']: 122 for p in list(qq.routes.keys()): 123 self.assertTrue(p in paths) 124 else: 125 for p in list(qq.routes.keys()): 126 if self.gobgp.peers[qq]['is_rr_client']: 127 self.assertTrue(p in paths) 128 else: 129 self.assertFalse(p in paths) 130 131 def test_10_setup_rr_rtc_isolation_policy(self): 132 # +-------+ 133 # | rr | 134 # +----------------+----| (RR) |---+----------------+ 135 # | | +-------+ | | 136 # | | | | 137 # (iBGP) (iBGP) (iBGP) (iBGP) 138 # | | | | 139 # +-------------+ +-------------+ +-------------+ +-------------+ 140 # | acme1 | | acme2 | | tyrell1 | | tyrell2 | 141 # | (RR Client) | | (RR Client) | | (RR Client) | | (RR Client) | 142 # +-------------+ +-------------+ +-------------+ +-------------+ 143 144 gobgp_ctn_image_name = parser_option.gobgp_image 145 rr = GoBGPContainer(name='rr', asn=65000, router_id='192.168.1.1', 146 ctn_image_name=gobgp_ctn_image_name, 147 log_level=parser_option.gobgp_log_level) 148 acme1 = GoBGPContainer(name='acme1', asn=65000, router_id='192.168.1.101', 149 ctn_image_name=gobgp_ctn_image_name, 150 log_level=parser_option.gobgp_log_level) 151 acme2 = GoBGPContainer(name='acme2', asn=65000, router_id='192.168.1.102', 152 ctn_image_name=gobgp_ctn_image_name, 153 log_level=parser_option.gobgp_log_level) 154 155 tyrell1 = GoBGPContainer(name='tyrell1', asn=65000, router_id='192.168.1.201', 156 ctn_image_name=gobgp_ctn_image_name, 157 log_level=parser_option.gobgp_log_level) 158 159 tyrell2 = GoBGPContainer(name='tyrell2', asn=65000, router_id='192.168.1.202', 160 ctn_image_name=gobgp_ctn_image_name, 161 log_level=parser_option.gobgp_log_level) 162 163 time.sleep(max(ctn.run() for ctn in [rr, acme1, acme2, tyrell1, tyrell2])) 164 165 rr.add_peer( 166 acme1, 167 vpn=True, 168 addpath=16, 169 graceful_restart=True, 170 llgr=True, 171 is_rr_client=True, 172 ) 173 acme1.add_peer(rr, vpn=True, addpath=16, graceful_restart=True, llgr=True) 174 175 rr.add_peer( 176 acme2, 177 vpn=True, 178 addpath=16, 179 graceful_restart=True, 180 llgr=True, 181 is_rr_client=True, 182 ) 183 acme2.add_peer(rr, vpn=True, addpath=16, graceful_restart=True, llgr=True) 184 185 rr.add_peer( 186 tyrell1, 187 vpn=True, 188 addpath=16, 189 graceful_restart=True, 190 llgr=True, 191 is_rr_client=True, 192 ) 193 tyrell1.add_peer(rr, vpn=True, addpath=16, graceful_restart=True, llgr=True) 194 195 rr.add_peer( 196 tyrell2, 197 vpn=True, 198 addpath=16, 199 graceful_restart=True, 200 llgr=True, 201 is_rr_client=True, 202 ) 203 tyrell2.add_peer(rr, vpn=True, addpath=16, graceful_restart=True, llgr=True) 204 205 self.__class__.rr = rr 206 self.__class__.acme1 = acme1 207 self.__class__.acme2 = acme2 208 self.__class__.tyrell1 = tyrell1 209 self.__class__.tyrell2 = tyrell2 210 211 # add import/export policy to allow peers exchange routes within specific RTs 212 # later tests should not break due to RTC Updates being filtered-out 213 214 rr.local("gobgp policy neighbor add clients-acme {} {}".format( 215 rr.peer_name(acme1), 216 rr.peer_name(acme2))) 217 218 rr.local("gobgp policy neighbor add clients-tyrell {} {}".format( 219 rr.peer_name(tyrell1), 220 rr.peer_name(tyrell2))) 221 222 rr.local("gobgp policy ext-community add rts-acme rt:^100:.*$") 223 rr.local("gobgp policy ext-community add rts-tyrell rt:^200:.*$") 224 225 rr.local('gobgp policy statement add allow-rtc') 226 rr.local('gobgp policy statement allow-rtc add condition afi-safi-in rtc') 227 rr.local('gobgp policy statement allow-rtc add action accept') 228 229 rr.local('gobgp policy statement add allow-acme') 230 rr.local('gobgp policy statement allow-acme add condition neighbor clients-acme') 231 rr.local('gobgp policy statement allow-acme add condition ext-community rts-acme') 232 rr.local('gobgp policy statement allow-acme add action accept') 233 234 rr.local('gobgp policy statement add allow-tyrell') 235 rr.local('gobgp policy statement allow-tyrell add condition neighbor clients-tyrell') 236 rr.local('gobgp policy statement allow-tyrell add condition ext-community rts-tyrell') 237 rr.local('gobgp policy statement allow-tyrell add action accept') 238 rr.local('gobgp policy add tenancy allow-rtc allow-acme allow-tyrell') 239 240 rr.local('gobgp global policy import add tenancy default reject') 241 rr.local('gobgp global policy export add tenancy default reject') 242 243 acme1.local("gobgp vrf add a1 rd 100:100 rt both 100:100") 244 acme2.local("gobgp vrf add a1 rd 100:100 rt both 100:100") 245 246 tyrell1.local("gobgp vrf add t1 rd 200:100 rt both 200:100") 247 tyrell2.local("gobgp vrf add t1 rd 200:100 rt both 200:100") 248 249 rr.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=acme1) 250 rr.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=acme2) 251 rr.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=tyrell1) 252 rr.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=tyrell2) 253 254 def test_11_routes_in_allowed_acme_rts_are_exchanged(self): 255 self.acme1.local("gobgp vrf a1 rib add 10.10.0.0/16 local-pref 100") 256 self.acme2.local("gobgp vrf a1 rib add 10.20.0.0/16") 257 self.tyrell1.local("gobgp vrf t1 rib add 20.10.0.0/16") 258 self.tyrell2.local("gobgp vrf t1 rib add 20.20.0.0/16") 259 time.sleep(1) 260 261 self.assert_adv_count(self.rr, self.acme1, 'rtc', 2) 262 self.assert_adv_count(self.rr, self.acme1, 'ipv4-l3vpn', 1) 263 self.assert_adv_count(self.rr, self.acme2, 'rtc', 2) 264 self.assert_adv_count(self.rr, self.acme2, 'ipv4-l3vpn', 1) 265 self.assert_adv_count(self.rr, self.tyrell1, 'rtc', 2) 266 self.assert_adv_count(self.rr, self.tyrell1, 'ipv4-l3vpn', 1) 267 self.assert_adv_count(self.rr, self.tyrell2, 'rtc', 2) 268 self.assert_adv_count(self.rr, self.tyrell2, 'ipv4-l3vpn', 1) 269 270 def test_12_routes_from_separate_rts_peers_are_isolated_by_rr(self): 271 self.tyrell1.local("gobgp vrf add a1 rd 100:100 rt both 100:100") 272 self.tyrell1.local("gobgp vrf a1 rib add 10.10.0.0/16 local-pref 200") 273 self.tyrell1.local("gobgp vrf a1 rib add 10.30.0.0/16") 274 time.sleep(1) 275 276 rr_t2_in = self.rr.get_adj_rib_in(self.tyrell1, rf='ipv4-l3vpn') 277 self.assertEqual(3, len(rr_t2_in)) 278 279 rr_a2_out = self.rr.get_adj_rib_out(self.acme2, rf='ipv4-l3vpn') 280 self.assertEqual(1, len(rr_a2_out)) 281 282 a2_routes = self.acme2.get_adj_rib_in(self.rr, rf='ipv4-l3vpn') 283 self.assertEqual(1, len(a2_routes)) 284 ar0 = a2_routes[0] 285 self.assertEqual('10.10.0.0/16', ar0['prefix']) 286 self.assertEqual(self.rr.peer_name(self.acme1), ar0['nexthop']) 287 self.assertEqual(100, ar0['local-pref']) 288 289 290 if __name__ == '__main__': 291 output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True) 292 if int(output) != 0: 293 print("docker not found") 294 sys.exit(1) 295 296 nose.main(argv=sys.argv, addplugins=[OptionParser()], 297 defaultTest=sys.argv[0])