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])