github.com/osrg/gobgp/v3@v3.30.0/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  
    17  import json
    18  import sys
    19  import time
    20  import unittest
    21  
    22  import collections
    23  collections.Callable = collections.abc.Callable
    24  
    25  import nose
    26  
    27  from lib.noseplugin import OptionParser, parser_option
    28  
    29  from lib import base
    30  from lib.base import (
    31      BGP_FSM_IDLE,
    32      BGP_FSM_ACTIVE,
    33      BGP_FSM_ESTABLISHED,
    34      BGP_ATTR_TYPE_MULTI_EXIT_DISC,
    35      BGP_ATTR_TYPE_LOCAL_PREF,
    36      wait_for_completion,
    37      assert_several_times,
    38      local,
    39  )
    40  from lib.gobgp import (
    41      GoBGPContainer,
    42      extract_path_attribute,
    43  )
    44  from lib.quagga import QuaggaBGPContainer
    45  from lib.exabgp import ExaBGPContainer
    46  
    47  
    48  class GoBGPTestBase(unittest.TestCase):
    49  
    50      @classmethod
    51      def setUpClass(cls):
    52          gobgp_ctn_image_name = parser_option.gobgp_image
    53          base.TEST_PREFIX = parser_option.test_prefix
    54  
    55          g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1',
    56                              ctn_image_name=gobgp_ctn_image_name,
    57                              log_level=parser_option.gobgp_log_level)
    58          q1 = QuaggaBGPContainer(name='q1', asn=65001, router_id='192.168.0.2')
    59          q2 = QuaggaBGPContainer(name='q2', asn=65002, router_id='192.168.0.3')
    60          q3 = QuaggaBGPContainer(name='q3', asn=65003, router_id='192.168.0.4')
    61  
    62          qs = [q1, q2, q3]
    63          ctns = [g1, q1, q2, q3]
    64  
    65          initial_wait_time = max(ctn.run() for ctn in ctns)
    66          time.sleep(initial_wait_time)
    67  
    68          for q in qs:
    69              g1.add_peer(q, passwd='passwd')
    70              q.add_peer(g1, passwd='passwd', passive=True)
    71  
    72          # advertise a route from q1, q2, q3
    73          for idx, q in enumerate(qs):
    74              route = '10.0.{0}.0/24'.format(idx + 1)
    75              q.add_route(route)
    76  
    77          cls.gobgp = g1
    78          cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3}
    79  
    80      # test each neighbor state is turned establish
    81      def test_01_neighbor_established(self):
    82          for q in self.quaggas.values():
    83              self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q)
    84  
    85      def test_02_check_gobgp_global_rib(self):
    86          for q in self.quaggas.values():
    87              # paths expected to exist in gobgp's global rib
    88              routes = list(q.routes.keys())
    89              timeout = 120
    90              interval = 1
    91              count = 0
    92  
    93              while True:
    94                  # gobgp's global rib
    95                  state = self.gobgp.get_neighbor_state(q)
    96                  self.assertEqual(state, BGP_FSM_ESTABLISHED)
    97                  global_rib = [p['prefix'] for p in self.gobgp.get_global_rib()]
    98  
    99                  for p in global_rib:
   100                      if p in routes:
   101                          routes.remove(p)
   102  
   103                  if len(routes) == 0:
   104                      break
   105  
   106                  time.sleep(interval)
   107                  count += interval
   108                  if count >= timeout:
   109                      raise Exception('timeout')
   110  
   111      # check gobgp properly add it's own asn to aspath
   112      def test_03_check_gobgp_adj_out_rib(self):
   113          for q in self.quaggas.values():
   114              for path in self.gobgp.get_adj_rib_out(q):
   115                  asns = path['aspath']
   116                  self.assertTrue(self.gobgp.asn in asns)
   117  
   118      # check routes are properly advertised to all BGP speaker
   119      def test_04_check_quagga_global_rib(self):
   120          interval = 1
   121          timeout = int(120 / interval)
   122          for q in self.quaggas.values():
   123              done = False
   124              for _ in range(timeout):
   125                  if done:
   126                      break
   127                  global_rib = q.get_global_rib()
   128                  global_rib = [p['prefix'] for p in global_rib]
   129                  if len(global_rib) < len(self.quaggas):
   130                      time.sleep(interval)
   131                      continue
   132  
   133                  self.assertEqual(len(global_rib), len(self.quaggas))
   134  
   135                  for c in self.quaggas.values():
   136                      for r in c.routes:
   137                          self.assertTrue(r in global_rib)
   138                  done = True
   139              if done:
   140                  continue
   141              # should not reach here
   142              raise AssertionError
   143  
   144      def test_05_add_quagga(self):
   145          q4 = QuaggaBGPContainer(name='q4', asn=65004, router_id='192.168.0.5')
   146          self.quaggas['q4'] = q4
   147          initial_wait_time = q4.run()
   148          time.sleep(initial_wait_time)
   149  
   150          self.gobgp.add_peer(q4)
   151          q4.add_peer(self.gobgp)
   152  
   153          q4.add_route('10.0.4.0/24')
   154  
   155          self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q4)
   156  
   157      def test_06_check_global_rib(self):
   158          self.test_02_check_gobgp_global_rib()
   159          self.test_04_check_quagga_global_rib()
   160  
   161      def test_07_stop_one_quagga(self):
   162          g1 = self.gobgp
   163          q4 = self.quaggas['q4']
   164          q4.stop()
   165          self.gobgp.wait_for(expected_state=BGP_FSM_ACTIVE, peer=q4)
   166  
   167          g1.del_peer(q4)
   168          del self.quaggas['q4']
   169  
   170      # check gobgp properly send withdrawal message with q4's route
   171      def test_08_check_global_rib(self):
   172          self.test_02_check_gobgp_global_rib()
   173          self.test_04_check_quagga_global_rib()
   174  
   175      def test_09_add_distant_relative(self):
   176          q1 = self.quaggas['q1']
   177          q2 = self.quaggas['q2']
   178          q3 = self.quaggas['q3']
   179          q5 = QuaggaBGPContainer(name='q5', asn=65005, router_id='192.168.0.6')
   180  
   181          initial_wait_time = q5.run()
   182          time.sleep(initial_wait_time)
   183  
   184          for q in [q2, q3]:
   185              q5.add_peer(q)
   186              q.add_peer(q5)
   187  
   188          med200 = {'name': 'med200',
   189                    'type': 'permit',
   190                    'match': '0.0.0.0/0',
   191                    'med': 200}
   192          q2.add_policy(med200, self.gobgp, 'out')
   193          med100 = {'name': 'med100',
   194                    'type': 'permit',
   195                    'match': '0.0.0.0/0',
   196                    'med': 100}
   197          q3.add_policy(med100, self.gobgp, 'out')
   198  
   199          q5.add_route('10.0.6.0/24')
   200  
   201          self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q2)
   202          self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q3)
   203          q2.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q5)
   204          q3.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q5)
   205  
   206          timeout = 120
   207          interval = 1
   208          count = 0
   209          while True:
   210              paths = self.gobgp.get_adj_rib_out(q1, '10.0.6.0/24')
   211              if len(paths) > 0:
   212                  path = paths[0]
   213                  print("{0}'s nexthop is {1}".format(path['nlri']['prefix'],
   214                                                      path['nexthop']))
   215                  n_addrs = [i[1].split('/')[0] for i in self.gobgp.ip_addrs]
   216                  if path['nexthop'] in n_addrs:
   217                      break
   218  
   219              time.sleep(interval)
   220              count += interval
   221              if count >= timeout:
   222                  raise Exception('timeout')
   223  
   224      def test_10_originate_path(self):
   225          self.gobgp.add_route('10.10.0.0/24')
   226          dst = self.gobgp.get_global_rib('10.10.0.0/24')
   227          self.assertEqual(len(dst), 1)
   228          self.assertEqual(len(dst[0]['paths']), 1)
   229          path = dst[0]['paths'][0]
   230          self.assertEqual(path['nexthop'], '0.0.0.0')
   231          self.assertEqual(len(path['aspath']), 0)
   232  
   233      def test_11_check_adj_rib_out(self):
   234          for q in self.quaggas.values():
   235              paths = self.gobgp.get_adj_rib_out(q, '10.10.0.0/24')
   236              self.assertEqual(len(paths), 1)
   237              path = paths[0]
   238              peer_info = self.gobgp.peers[q]
   239              local_addr = peer_info['local_addr'].split('/')[0]
   240              self.assertEqual(path['nexthop'], local_addr)
   241              self.assertEqual(path['aspath'], [self.gobgp.asn])
   242  
   243      def test_12_disable_peer(self):
   244          q1 = self.quaggas['q1']
   245          self.gobgp.disable_peer(q1)
   246          self.gobgp.wait_for(expected_state=BGP_FSM_IDLE, peer=q1)
   247  
   248          time.sleep(3)
   249  
   250          for route in q1.routes.keys():
   251              dst = self.gobgp.get_global_rib(route)
   252              self.assertEqual(len(dst), 0)
   253  
   254              for q in self.quaggas.values():
   255                  if q is q1:
   256                      continue
   257                  paths = self.gobgp.get_adj_rib_out(q, route)
   258                  self.assertEqual(len(paths), 0)
   259  
   260      def test_13_enable_peer(self):
   261          q1 = self.quaggas['q1']
   262          self.gobgp.enable_peer(q1)
   263          self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q1)
   264  
   265      def test_14_check_adj_rib_out(self):
   266          self.test_11_check_adj_rib_out()
   267  
   268      def test_15_check_active_connection(self):
   269          g1 = self.gobgp
   270          g2 = GoBGPContainer(name='g2', asn=65000, router_id='192.168.0.7',
   271                              ctn_image_name=self.gobgp.image,
   272                              log_level=parser_option.gobgp_log_level)
   273          time.sleep(g2.run())
   274          self.quaggas['g2'] = g2
   275          g2.add_peer(g1, passive=True)
   276          g1.add_peer(g2)
   277          g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=g2)
   278  
   279      def test_16_check_local_pref_and_med_handling(self):
   280          g1 = self.gobgp
   281          g1.add_route('10.20.0.0/24', local_pref=1000, med=2000)
   282          # iBGP peer
   283          g2 = self.quaggas['g2']
   284          paths = g2.get_global_rib('10.20.0.0/24')
   285          self.assertEqual(len(paths), 1)
   286          self.assertEqual(len(paths[0]['paths']), 1)
   287          path = paths[0]['paths'][0]
   288          local_pref = extract_path_attribute(path, BGP_ATTR_TYPE_LOCAL_PREF)
   289          self.assertEqual(local_pref['value'], 1000)
   290          med = extract_path_attribute(path, BGP_ATTR_TYPE_MULTI_EXIT_DISC)
   291          self.assertEqual(med['metric'], 2000)
   292  
   293          # eBGP peer
   294          q1 = self.quaggas['q1']
   295          paths = q1.get_global_rib('10.20.0.0/24')
   296          self.assertEqual(len(paths), 1)
   297          path = paths[0]
   298          local_pref = extract_path_attribute(path, BGP_ATTR_TYPE_LOCAL_PREF)
   299          # local_pref's default value is 100
   300          self.assertEqual(local_pref['value'], 100)
   301          med = extract_path_attribute(path, BGP_ATTR_TYPE_MULTI_EXIT_DISC)
   302          self.assertEqual(med['metric'], 2000)
   303  
   304      def test_17_check_shutdown(self):
   305          g1 = self.gobgp
   306          q1 = self.quaggas['q1']
   307          q2 = self.quaggas['q2']
   308          q3 = self.quaggas['q3']
   309  
   310          q2.add_route('20.0.0.0/24')
   311          q3.add_route('20.0.0.0/24')
   312  
   313          self.test_01_neighbor_established()
   314  
   315          self.test_02_check_gobgp_global_rib()
   316  
   317          paths = q1.get_global_rib('20.0.0.0/24')
   318          self.assertEqual(len(paths), 1)
   319          n_addrs = [i[1].split('/')[0] for i in self.gobgp.ip_addrs]
   320          self.assertIn(paths[0]['nexthop'], n_addrs)
   321  
   322          q3.stop()
   323  
   324          self.gobgp.wait_for(expected_state=BGP_FSM_ACTIVE, peer=q3)
   325  
   326          def f():
   327              paths = q1.get_global_rib('20.0.0.0/24')
   328              self.assertEqual(len(paths), 1)
   329              self.assertIn(paths[0]['nexthop'], n_addrs)
   330  
   331          assert_several_times(f)
   332  
   333          g1.del_peer(q3)
   334          del self.quaggas['q3']
   335  
   336      def test_18_check_withdrawal(self):
   337          g1 = self.gobgp
   338          q1 = self.quaggas['q1']
   339          q2 = self.quaggas['q2']
   340  
   341          g1.add_route('30.0.0.0/24')
   342          q1.add_route('30.0.0.0/24')
   343  
   344          self.test_01_neighbor_established()
   345  
   346          self.test_02_check_gobgp_global_rib()
   347  
   348          paths = g1.get_adj_rib_out(q1, '30.0.0.0/24')
   349          self.assertEqual(len(paths), 1)
   350          self.assertNotIn('source-id', paths[0])
   351          paths = g1.get_adj_rib_out(q2, '30.0.0.0/24')
   352          self.assertEqual(len(paths), 1)
   353          self.assertNotIn('source-id', paths[0])
   354  
   355          g1.local('gobgp global rib del 30.0.0.0/24')
   356  
   357          def f():
   358              paths = g1.get_adj_rib_out(q1, '30.0.0.0/24')
   359              self.assertEqual(len(paths), 0)
   360              paths = g1.get_adj_rib_out(q2, '30.0.0.0/24')
   361              self.assertEqual(len(paths), 1)
   362              self.assertEqual(paths[0]['source-id'], '192.168.0.2')
   363  
   364          assert_several_times(f)
   365  
   366      def test_19_check_grpc_add_neighbor(self):
   367          g1 = self.gobgp
   368          e1 = ExaBGPContainer(name='e1', asn=65000, router_id='192.168.0.7')
   369          time.sleep(e1.run())
   370          e1.add_peer(g1)
   371          self.quaggas['e1'] = e1
   372          n = e1.peers[g1]['local_addr'].split('/')[0]
   373          g1.local('gobgp n add {0} as 65000'.format(n))
   374          g1.add_peer(e1, reload_config=False)
   375  
   376          g1.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=e1)
   377  
   378      def test_20_check_grpc_del_neighbor(self):
   379          g1 = self.gobgp
   380          e1 = self.quaggas['e1']
   381          n = e1.peers[g1]['local_addr'].split('/')[0]
   382          g1.local('gobgp n del {0}'.format(n))
   383          g1.del_peer(e1, reload_config=False)
   384  
   385      def test_21_check_withdrawal_2(self):
   386          g1 = self.gobgp
   387          g2 = self.quaggas['g2']
   388  
   389          prefix = '40.10.0.0/24'
   390          g1.add_route(prefix)
   391          wait_for_completion(lambda: len(g1.get_global_rib(prefix)) == 1)
   392          wait_for_completion(lambda: len(g2.get_global_rib(prefix)) == 1)
   393  
   394          r = g2.local('gobgp monitor global rib -j', stream=True, tty=False)
   395  
   396          g1.local('gobgp global rib del 40.10.0.0/24')
   397          del g1.routes[prefix]
   398  
   399          wait_for_completion(lambda: len(g1.get_global_rib(prefix)) == 0)
   400          wait_for_completion(lambda: len(g2.get_global_rib(prefix)) == 0)
   401  
   402          ret = json.loads(next(r))
   403          self.assertEqual(ret[0]['nlri']['prefix'], prefix)
   404          self.assertTrue('withdrawal' in ret[0])
   405  
   406      def test_22_check_cli_sorted(self):
   407          g1 = self.gobgp
   408          cnt = 0
   409  
   410          def next_prefix():
   411              for i in range(100, 105):
   412                  for j in range(100, 105):
   413                      yield '{0}.{1}.0.0/24'.format(i, j)
   414  
   415          for p in next_prefix():
   416              g1.local('gobgp global rib add {0}'.format(p))
   417              cnt += 1
   418  
   419          cnt2 = 0
   420          g = next_prefix()
   421          n = next(g)
   422          for path in g1.local("gobgp global rib", capture=True).split('\n')[1:]:
   423              if [elem for elem in path.split(' ') if elem != ''][1] == n:
   424                  try:
   425                      cnt2 += 1
   426                      n = next(g)
   427                  except StopIteration:
   428                      break
   429  
   430          self.assertEqual(cnt, cnt2)
   431  
   432      def test_23_check_withdrawal3(self):
   433          gobgp_ctn_image_name = parser_option.gobgp_image
   434          g1 = self.gobgp
   435          g3 = GoBGPContainer(name='g3', asn=65006, router_id='192.168.0.8',
   436                              ctn_image_name=gobgp_ctn_image_name,
   437                              log_level=parser_option.gobgp_log_level)
   438          g4 = GoBGPContainer(name='g4', asn=65007, router_id='192.168.0.9',
   439                              ctn_image_name=gobgp_ctn_image_name,
   440                              log_level=parser_option.gobgp_log_level)
   441  
   442          initial_wait_time = max(ctn.run() for ctn in [g3, g4])
   443          time.sleep(initial_wait_time)
   444  
   445          self.quaggas = {'g3': g3, 'g4': g4}
   446  
   447          g3.local('gobgp global rib add 50.0.0.0/24')
   448  
   449          g1.add_peer(g3, passive=True)
   450          g3.add_peer(g1)
   451          g1.add_peer(g4, passive=True)
   452          g4.add_peer(g1)
   453  
   454          self.test_01_neighbor_established()
   455  
   456          self.test_02_check_gobgp_global_rib()
   457  
   458          g4.local('gobgp global rib add 50.0.0.0/24 med 10')
   459  
   460          paths = g1.get_adj_rib_out(g3, '50.0.0.0/24')
   461          self.assertEqual(len(paths), 0)
   462          paths = g1.get_adj_rib_out(g4, '50.0.0.0/24')
   463          self.assertEqual(len(paths), 1)
   464          self.assertEqual(paths[0]['source-id'], '192.168.0.8')
   465  
   466          g3.local('gobgp global rib del 50.0.0.0/24')
   467  
   468          paths = g1.get_adj_rib_out(g3, '50.0.0.0/24')
   469          self.assertEqual(len(paths), 1)
   470          self.assertEqual(paths[0]['source-id'], '192.168.0.9')
   471          paths = g1.get_adj_rib_out(g4, '50.0.0.0/24')
   472          self.assertEqual(len(paths), 0)
   473  
   474      def test_24_add_quagga_md5(self):
   475          q6 = QuaggaBGPContainer(name='q6', asn=65004, router_id='192.168.0.5')
   476          self.quaggas['q6'] = q6
   477          initial_wait_time = q6.run()
   478          time.sleep(initial_wait_time)
   479  
   480          self.gobgp.add_peer(q6, passwd='password')
   481          q6.add_peer(self.gobgp, passwd='password')
   482  
   483          self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q6)
   484  
   485      def test_25_add_quagga_md5_badpass(self):
   486          q7 = QuaggaBGPContainer(name='q6', asn=65004, router_id='192.168.0.7')
   487          self.quaggas['q7'] = q7
   488          initial_wait_time = q7.run()
   489          time.sleep(initial_wait_time)
   490  
   491          self.gobgp.add_peer(q7, passwd='password')
   492          q7.add_peer(self.gobgp, passwd='passwordBAD')
   493  
   494          time.sleep(10)
   495          state = self.gobgp.get_neighbor_state(q7)
   496          # should not establish
   497          self.assertEqual(state, BGP_FSM_ACTIVE)
   498  
   499      def test_26_dynamic_peer(self):
   500          gobgp_ctn_image_name = parser_option.gobgp_image
   501          g5 = GoBGPContainer(name='g5', asn=65008, router_id='192.168.0.8',
   502                              ctn_image_name=gobgp_ctn_image_name,
   503                              log_level=parser_option.gobgp_log_level,
   504                              bgp_config={
   505                                  'peer-groups': [
   506                                      {
   507                                          'config':{
   508                                              'peer-group-name':'PG1',
   509                                              'peer-as': 65004,
   510                                              }
   511                                          }
   512                                      ],
   513                                  'dynamic-neighbors':[
   514                                      {
   515                                          'config': {
   516                                              'prefix':'172.17.0.0/16',
   517                                              'peer-group':'PG1'
   518                                              }
   519                                          }
   520                                      
   521                                      ]
   522                                  })
   523          q8 = QuaggaBGPContainer(name='q8', asn=65004, router_id='192.168.0.5')
   524          self.quaggas = { 'g8': q8 }
   525  
   526          time.sleep(g5.run())
   527          initial_wait_time = q8.run()
   528          time.sleep(initial_wait_time)
   529  
   530          q8.add_peer(g5)
   531  
   532          time.sleep(initial_wait_time)
   533  
   534          # should establish via the dynamic link
   535          # note that we are checking it on the Quagga side since GoBGP
   536          # config doesn't explicitly "know about" the peer
   537          q8.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=g5)
   538  
   539      def test_27_dynamic_peer_md5(self):
   540          gobgp_ctn_image_name = parser_option.gobgp_image
   541          g6 = GoBGPContainer(name='g6', asn=65008, router_id='192.168.0.9',
   542                              ctn_image_name=gobgp_ctn_image_name,
   543                              log_level=parser_option.gobgp_log_level,
   544                              bgp_config={
   545                                  'peer-groups': [
   546                                      {
   547                                          'config':{
   548                                              'peer-group-name':'PG1',
   549                                              'peer-as': 65004,
   550                                              'auth-password': 'password',
   551                                              }
   552                                          }
   553                                      ],
   554                                  'dynamic-neighbors':[
   555                                      {
   556                                          'config': {
   557                                              'prefix':'172.17.0.0/16',
   558                                              'peer-group':'PG1'
   559                                              }
   560                                          }
   561                                      
   562                                      ]
   563                                  })
   564          q9 = QuaggaBGPContainer(name='q9', asn=65004, router_id='192.168.0.10')
   565          self.quaggas = { 'g9': q9 }
   566  
   567          time.sleep(g6.run())
   568          initial_wait_time = q9.run()
   569          time.sleep(initial_wait_time)
   570  
   571          q9.add_peer(g6, passwd='password', passive=False)
   572  
   573          # should establish via the dynamic link
   574          # note that we are checking it on the Quagga side since GoBGP
   575          # config doesn't explicitly "know about" the peer
   576          q9.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=g6, timeout=30)
   577  
   578  
   579  if __name__ == '__main__':
   580      output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True)
   581      if int(output) != 0:
   582          print("docker not found")
   583          sys.exit(1)
   584  
   585      nose.main(argv=sys.argv, addplugins=[OptionParser()],
   586                defaultTest=sys.argv[0])