github.com/osrg/gobgp/v3@v3.30.0/test/scenario_test/addpath_test.py (about) 1 # Copyright (C) 2016 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 import sys 17 import json 18 import time 19 import unittest 20 21 import collections 22 collections.Callable = collections.abc.Callable 23 24 import nose 25 26 from lib import base 27 from lib.base import ( 28 BGP_FSM_ESTABLISHED, 29 assert_several_times, 30 local, 31 ) 32 from lib.gobgp import GoBGPContainer 33 from lib.exabgp import ExaBGPContainer 34 from lib.noseplugin import OptionParser, parser_option 35 36 37 class GoBGPTestBase(unittest.TestCase): 38 SEND_MAX = 5 39 INSTALLED_PATHS = SEND_MAX + 1 40 41 @classmethod 42 def setUpClass(cls): 43 gobgp_ctn_image_name = parser_option.gobgp_image 44 base.TEST_PREFIX = parser_option.test_prefix 45 46 g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1', 47 ctn_image_name=gobgp_ctn_image_name, 48 log_level=parser_option.gobgp_log_level) 49 g2 = GoBGPContainer(name='g2', asn=65000, router_id='192.168.0.2', 50 ctn_image_name=gobgp_ctn_image_name, 51 log_level=parser_option.gobgp_log_level) 52 g3 = GoBGPContainer(name='g3', asn=65000, router_id='192.168.0.3', 53 ctn_image_name=gobgp_ctn_image_name, 54 log_level=parser_option.gobgp_log_level) 55 g4 = GoBGPContainer(name="g4", asn=65000, router_id="192.168.0.4", 56 ctn_image_name=gobgp_ctn_image_name, 57 log_level=parser_option.gobgp_log_level) 58 g5 = GoBGPContainer(name="g5", asn=65000,router_id="192.168.0.5", 59 ctn_image_name=gobgp_ctn_image_name, 60 log_level=parser_option.gobgp_log_level) 61 e1 = ExaBGPContainer(name="e1", asn=65000, router_id="192.168.0.6") 62 63 ctns = [g1, g2, g3, g4, g5, e1] 64 initial_wait_time = max(ctn.run() for ctn in ctns) 65 66 time.sleep(initial_wait_time) 67 68 g1.add_peer(e1, addpath=16) 69 e1.add_peer(g1, addpath=16) 70 71 g1.add_peer(g2, is_rr_client=True) 72 g2.add_peer(g1) 73 74 g1.add_peer(g3, addpath=cls.SEND_MAX, is_rr_client=True) 75 g3.add_peer(g1, addpath=cls.SEND_MAX) 76 77 g4.add_peer(g5, addpath=cls.SEND_MAX) 78 g5.add_peer(g4, addpath=cls.SEND_MAX) 79 80 cls.g1 = g1 81 cls.g2 = g2 82 cls.g3 = g3 83 cls.g4 = g4 84 cls.g5 = g5 85 cls.e1 = e1 86 87 # test each neighbor state is turned establish 88 def test_00_neighbor_established(self): 89 self.g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=self.g2) 90 self.g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=self.g3) 91 self.g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=self.e1) 92 self.g4.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=self.g5) 93 94 # prepare routes with path_id (no error check) 95 def test_01_prepare_add_paths_routes(self): 96 aspath = [] 97 for i in range(self.INSTALLED_PATHS): 98 aspath.append((i + 1) * 100) 99 self.e1.add_route( 100 route="192.168.100.0/24", 101 identifier=(i + 1) * 10, 102 aspath=aspath, 103 ) 104 105 # update multiple time the same route 106 # and expect the route counter to increment only once 107 for i in range(self.INSTALLED_PATHS): 108 self.g4.add_route( 109 route="192.168.100.0/24", 110 identifier=10, 111 local_pref=i + 100 + 1, 112 ) 113 114 def test_02_check_g4_adj_out(self): 115 def f(): 116 # the last update should have been received by g5 117 rib = self.g4.get_adj_rib_out(self.g5, add_path_enabled=True) 118 self.assertEqual(len(rib), 1) 119 self.assertEqual(len(rib[0]["paths"]), 1) 120 self.assertEqual( 121 rib[0]["paths"][0]["local-pref"], 100 + self.INSTALLED_PATHS 122 ) 123 self.assertFalse(rib[0]["paths"][0].get("send-max-filtered", False)) 124 125 assert_several_times(f) 126 127 def test_03_check_g5_global_rib(self): 128 def f(): 129 # the last update should have been received by g5 130 rib = self.g5.get_global_rib() 131 self.assertEqual(len(rib), 1) 132 self.assertEqual(len(rib[0]["paths"]), 1) 133 self.assertEqual( 134 rib[0]["paths"][0]["local-pref"], 100 + self.INSTALLED_PATHS 135 ) 136 137 assert_several_times(f) 138 139 # test INSTALLED_PATHS routes are installed to the rib due to add-path feature 140 def test_04_check_g1_global_rib(self): 141 def f(): 142 rib = self.g1.get_global_rib() 143 self.assertEqual(len(rib), 1) 144 self.assertEqual(len(rib[0]["paths"]), self.INSTALLED_PATHS) 145 146 assert_several_times(f) 147 148 # test only the best path is advertised to g2 149 def test_05_check_g2_global_rib(self): 150 def f(): 151 rib = self.g2.get_global_rib() 152 self.assertEqual(len(rib), 1) 153 self.assertEqual(len(rib[0]['paths']), 1) 154 self.assertEqual(len(rib[0]["paths"][0]["aspath"]), 1) 155 156 assert_several_times(f) 157 158 # test SEND_MAX routes are advertised to g3 159 def test_06_check_g3_global_rib(self): 160 def f(): 161 rib = self.g3.get_global_rib() 162 self.assertEqual(len(rib), 1) 163 self.assertEqual(len(rib[0]["paths"]), self.SEND_MAX) 164 165 assert_several_times(f) 166 167 def test_07_check_g1_adj_out(self): 168 adj_out = self.g1.get_adj_rib_out(self.g2, add_path_enabled=True) 169 self.assertEqual(len(adj_out), 1) 170 self.assertEqual(len(adj_out[0]["paths"]), 1) 171 172 adj_out = self.g1.get_adj_rib_out(self.g3, add_path_enabled=True) 173 self.assertEqual(len(adj_out), 1) 174 self.assertEqual(len(adj_out[0]["paths"]), self.INSTALLED_PATHS) 175 # expect the last path to be filtered 176 self.assertTrue(adj_out[0]["paths"][-1].get("send-max-filtered", False)) 177 178 # withdraw a route with path_id (no error check) 179 def test_08_withdraw_route_with_path_id(self): 180 self.e1.del_route(route="192.168.100.0/24", identifier=10) 181 182 # test the withdrawn route is removed from the rib 183 def test_09_check_g1_global_rib(self): 184 def f(): 185 rib = self.g1.get_global_rib() 186 self.assertEqual(len(rib), 1) 187 self.assertEqual(len(rib[0]["paths"]), self.INSTALLED_PATHS - 1) 188 # we deleted the highest priority path 189 for path in rib[0]['paths']: 190 self.assertTrue(2 <= len(path["aspath"]) <= self.INSTALLED_PATHS) 191 192 assert_several_times(f) 193 194 def test_10_check_g1_adj_out(self): 195 adj_out = self.g1.get_adj_rib_out(self.g2, add_path_enabled=True) 196 self.assertEqual(len(adj_out), 1) 197 self.assertEqual(len(adj_out[0]["paths"]), 1) 198 199 adj_out = self.g1.get_adj_rib_out(self.g3, add_path_enabled=True) 200 self.assertEqual(len(adj_out), 1) 201 self.assertEqual(len(adj_out[0]["paths"]), self.SEND_MAX) 202 # expect the last path to not be filtered 203 self.assertFalse(adj_out[0]["paths"][-1].get("send-max-filtered", False)) 204 205 # test the best path is replaced due to the removal from g1 rib 206 def test_11_check_g2_global_rib(self): 207 def f(): 208 rib = self.g2.get_global_rib() 209 self.assertEqual(len(rib), 1) 210 self.assertEqual(len(rib[0]['paths']), 1) 211 self.assertEqual(len(rib[0]["paths"][0]["aspath"]), 2) 212 213 assert_several_times(f) 214 215 # test the withdrawn route is removed from the rib of g3 216 # and the filtered route is advertised to g3 217 def test_12_check_g3_global_rib(self): 218 def f(): 219 rib = self.g3.get_global_rib() 220 self.assertEqual(len(rib), 1) 221 self.assertEqual(len(rib[0]["paths"]), self.SEND_MAX) 222 for path in rib[0]['paths']: 223 self.assertTrue(2 <= len(path["aspath"]) <= self.INSTALLED_PATHS) 224 225 assert_several_times(f) 226 227 # install a route with path_id via GoBGP CLI (no error check) 228 def test_13_install_add_paths_route_via_cli(self): 229 # identifier is duplicated with the identifier of the route from e1 230 self.g1.add_route(route='192.168.100.0/24', identifier=10, local_pref=500) 231 232 # test the route from CLI is installed to the rib 233 def test_14_check_g1_global_rib(self): 234 def f(): 235 rib = self.g1.get_global_rib() 236 self.assertEqual(len(rib), 1) 237 self.assertEqual(len(rib[0]["paths"]), self.INSTALLED_PATHS) 238 for path in rib[0]['paths']: 239 if not path["aspath"]: 240 self.assertEqual(path['local-pref'], 500) 241 else: 242 self.assertTrue(2 <= len(path["aspath"]) <= self.INSTALLED_PATHS) 243 244 assert_several_times(f) 245 246 def test_15_check_g1_adj_out(self): 247 adj_out = self.g1.get_adj_rib_out(self.g2, add_path_enabled=True) 248 self.assertEqual(len(adj_out), 1) 249 self.assertEqual(len(adj_out[0]["paths"]), 1) 250 251 adj_out = self.g1.get_adj_rib_out(self.g3, add_path_enabled=True) 252 self.assertEqual(len(adj_out), 1) 253 self.assertEqual(len(adj_out[0]["paths"]), self.INSTALLED_PATHS) 254 # the new best path shouldn't be advertised as it is added after 255 # the limit is reached 256 self.assertEqual(adj_out[0]["paths"][0]["local-pref"], 500) 257 self.assertTrue(adj_out[0]["paths"][0].get("send-max-filtered", False)) 258 259 # test the best path is replaced due to the CLI route from g1 rib 260 def test_16_check_g2_global_rib(self): 261 def f(): 262 rib = self.g2.get_global_rib() 263 self.assertEqual(len(rib), 1) 264 self.assertEqual(len(rib[0]['paths']), 1) 265 self.assertEqual(len(rib[0]["paths"][0]["aspath"]), 0) 266 self.assertEqual(rib[0]["paths"][0]["local-pref"], 500) 267 268 assert_several_times(f) 269 270 # test the route from CLI is advertised from g1 271 def test_17_check_g3_global_rib(self): 272 def f(): 273 rib = self.g3.get_global_rib() 274 self.assertEqual(len(rib), 1) 275 self.assertEqual(len(rib[0]["paths"]), self.SEND_MAX) 276 for path in rib[0]['paths']: 277 self.assertTrue(2 <= len(path["aspath"]) <= self.INSTALLED_PATHS) 278 279 assert_several_times(f) 280 281 # remove non-existing route with path_id via GoBGP CLI (no error check) 282 def test_18_remove_non_existing_add_paths_route_via_cli(self): 283 # specify locally non-existing identifier which has the same value 284 # with the identifier of the route from e1 285 self.g1.del_route(route='192.168.100.0/24', identifier=20) 286 287 # test none of route is removed by non-existing path_id via CLI 288 def test_19_check_g1_global_rib(self): 289 def f(): 290 rib = self.g1.get_global_rib() 291 self.assertEqual(len(rib), 1) 292 self.assertEqual(len(rib[0]["paths"]), self.INSTALLED_PATHS) 293 for path in rib[0]['paths']: 294 if not path["aspath"]: 295 self.assertEqual(path['local-pref'], 500) 296 else: 297 self.assertTrue(2 <= len(path["aspath"]) <= self.INSTALLED_PATHS) 298 299 assert_several_times(f) 300 301 # remove route with path_id via GoBGP CLI (no error check) 302 def test_20_remove_add_paths_route_via_cli(self): 303 self.g1.del_route(route='192.168.100.0/24', identifier=10) 304 305 def test_21_check_g1_adj_out(self): 306 adj_out = self.g1.get_adj_rib_out(self.g2, add_path_enabled=True) 307 self.assertEqual(len(adj_out), 1) 308 self.assertEqual(len(adj_out[0]["paths"]), 1) 309 310 adj_out = self.g1.get_adj_rib_out(self.g3, add_path_enabled=True) 311 self.assertEqual(len(adj_out), 1) 312 self.assertEqual(len(adj_out[0]["paths"]), self.INSTALLED_PATHS - 1) 313 314 # test the route is removed from the rib via CLI 315 def test_22_check_g1_global_rib(self): 316 def f(): 317 rib = self.g1.get_global_rib() 318 self.assertEqual(len(rib), 1) 319 self.assertEqual(len(rib[0]["paths"]), self.INSTALLED_PATHS - 1) 320 for path in rib[0]['paths']: 321 if not path["aspath"]: 322 self.assertTrue(2 <= len(path["aspath"]) <= self.INSTALLED_PATHS) 323 324 assert_several_times(f) 325 326 # test the best path is replaced the removal from g1 rib 327 def test_23_check_g2_global_rib(self): 328 def f(): 329 rib = self.g2.get_global_rib() 330 self.assertEqual(len(rib), 1) 331 self.assertEqual(len(rib[0]['paths']), 1) 332 self.assertEqual(len(rib[0]["paths"][0]["aspath"]), 2) 333 334 assert_several_times(f) 335 336 # test the removed route from CLI is withdrawn by g1 337 def test_24_check_g3_global_rib(self): 338 def f(): 339 rib = self.g3.get_global_rib() 340 self.assertEqual(len(rib), 1) 341 self.assertEqual(len(rib[0]["paths"]), self.SEND_MAX) 342 for path in rib[0]['paths']: 343 if not path["aspath"]: 344 self.assertTrue(2 <= len(path["aspath"]) <= self.INSTALLED_PATHS) 345 346 assert_several_times(f) 347 348 349 if __name__ == '__main__': 350 output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True) 351 if int(output) != 0: 352 print("docker not found") 353 sys.exit(1) 354 355 nose.main(argv=sys.argv, addplugins=[OptionParser()], 356 defaultTest=sys.argv[0])