text: # Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #    http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  # implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  import unittest
  from fabric.api import local
  from lib import base
  from lib.gobgp import *
  from lib.quagga import *
  import sys
  import os
  import time
  import nose
  from noseplugin import OptionParser, parser_option
  
  
  class GoBGPTestBase(unittest.TestCase):
  
      wait_per_retry = 5
      retry_limit = 15
  
      @classmethod
      def setUpClass(cls):
          gobgp_ctn_image_name = parser_option.gobgp_image
          base.TEST_PREFIX = parser_option.test_prefix
  
          g1 = GoBGPContainer(name='g1', asn=65000, router_id='192.168.0.1',
                              ctn_image_name=gobgp_ctn_image_name,
                              log_level=parser_option.gobgp_log_level)
  
          rs_clients = [QuaggaBGPContainer(name='q{0}'.format(i+1), asn=65001+i,
                        router_id='192.168.0.{0}'.format(i+2))
                        for i in range(3)]
          ctns = [g1] + rs_clients
          q1 = rs_clients[0]
          q2 = rs_clients[1]
          q3 = rs_clients[2]
  
          # advertise a route from route-server-clients
          routes = []
          for idx, rs_client in enumerate(rs_clients):
              route = '10.0.{0}.0/24'.format(idx+1)
              rs_client.add_route(route)
              routes.append(route)
  
          initial_wait_time = max(ctn.run() for ctn in ctns)
  
          time.sleep(initial_wait_time)
  
          for rs_client in rs_clients:
              g1.add_peer(rs_client, is_rs_client=True, passwd='passwd', passive=True, prefix_limit=10)
              rs_client.add_peer(g1, passwd='passwd')
  
          cls.gobgp = g1
          cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3}
  
      def check_gobgp_local_rib(self):
          for rs_client in self.quaggas.itervalues():
              done = False
              for _ in range(self.retry_limit):
                  if done:
                      break
                  local_rib = self.gobgp.get_local_rib(rs_client)
                  local_rib = [p['prefix'] for p in local_rib]
  
                  state = self.gobgp.get_neighbor_state(rs_client)
                  self.assertEqual(state, BGP_FSM_ESTABLISHED)
                  if len(local_rib) < len(self.quaggas)-1:
                      time.sleep(self.wait_per_retry)
                      continue
  
                  self.assertTrue(len(local_rib) == (len(self.quaggas)-1))
  
                  for c in self.quaggas.itervalues():
                      if rs_client != c:
                          for r in c.routes:
                              self.assertTrue(r in local_rib)
  
                  done = True
              if done:
                  continue
              # should not reach here
              self.assertTrue(False)
  
      def check_rs_client_rib(self):
          for rs_client in self.quaggas.itervalues():
              done = False
              for _ in range(self.retry_limit):
                  if done:
                      break
                  global_rib = rs_client.get_global_rib()
                  global_rib = [p['prefix'] for p in global_rib]
                  if len(global_rib) < len(self.quaggas):
                      time.sleep(self.wait_per_retry)
                      continue
  
                  self.assertTrue(len(global_rib) == len(self.quaggas))
  
                  for c in self.quaggas.itervalues():
                      for r in c.routes:
                          self.assertTrue(r in global_rib)
  
                  done = True
              if done:
                  continue
              # should not reach here
              self.assertTrue(False)
  
      # test each neighbor state is turned establish
      def test_01_neighbor_established(self):
          for q in self.quaggas.itervalues():
              self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q)
  
      # check advertised routes are stored in route-server's local-rib
      def test_02_check_gobgp_local_rib(self):
          self.check_gobgp_local_rib()
  
      # check gobgp's global rib. when configured as route-server, global rib
      # must be empty
      def test_03_check_gobgp_global_rib(self):
          self.assertTrue(len(self.gobgp.get_global_rib()) == 0)
  
      # check routes are properly advertised to route-server-client
      def test_04_check_rs_clients_rib(self):
          self.check_rs_client_rib()
  
      # check if quagga that is appended can establish connection with gobgp
      def test_05_add_rs_client(self):
          q4 = QuaggaBGPContainer(name='q4', asn=65004, router_id='192.168.0.5')
          self.quaggas['q4'] = q4
  
          route = '10.0.4.0/24'
          q4.add_route(route)
  
          initial_wait_time = q4.run()
          time.sleep(initial_wait_time)
          self.gobgp.add_peer(q4, is_rs_client=True)
          q4.add_peer(self.gobgp)
  
          self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q4)
  
      # check advertised routes are stored in gobgp's local-rib
      def test_05_check_gobgp_local_rib(self):
          self.check_gobgp_local_rib()
  
      # check routes are properly advertised to quagga
      def test_06_check_rs_clients_rib(self):
          self.check_rs_client_rib()
  
      def test_07_stop_one_rs_client(self):
          q4 = self.quaggas['q4']
          q4.stop()
          self.gobgp.wait_for(expected_state=BGP_FSM_ACTIVE, peer=q4)
  
          del self.quaggas['q4']
  
      # check a route advertised from q4 is deleted from gobgp's local-rib
      def test_08_check_gobgp_local_rib(self):
          self.check_gobgp_local_rib()
  
      # check whether gobgp properly sent withdrawal message with q4's route
      def test_09_check_rs_clients_rib(self):
          self.check_rs_client_rib()
  
      @unittest.skip("med shouldn't work with different AS peers by default")
      def test_10_add_distant_relative(self):
          q1 = self.quaggas['q1']
          q2 = self.quaggas['q2']
          q3 = self.quaggas['q3']
          q5 = QuaggaBGPContainer(name='q5', asn=65005, router_id='192.168.0.6')
  
          initial_wait_time = q5.run()
          time.sleep(initial_wait_time)
  
          for q in [q2, q3]:
              q5.add_peer(q)
              q.add_peer(q5)
  
          med200 = {'name': 'med200',
                    'type': 'permit',
                    'match': '0.0.0.0/0',
                    'med': 200,
                    'priority': 10}
          q2.add_policy(med200, self.gobgp, 'out')
          med100 = {'name': 'med100',
                    'type': 'permit',
                    'match': '0.0.0.0/0',
                    'med': 100,
                    'priority': 10}
          q3.add_policy(med100, self.gobgp, 'out')
  
          q5.add_route('10.0.6.0/24')
  
          q2.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q5)
          q3.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q5)
          self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q2)
          self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q3)
  
          def check_nexthop(target_prefix, expected_nexthop):
              done = False
              for _ in range(self.retry_limit):
                  if done:
                      break
                  time.sleep(self.wait_per_retry)
                  for path in q1.get_global_rib():
                      if path['prefix'] == target_prefix:
                          print "{0}'s nexthop is {1}".format(path['prefix'],
                                                              path['nexthop'])
                          n_addrs = [i[1].split('/')[0] for i in
                                     expected_nexthop.ip_addrs]
                          if path['nexthop'] in n_addrs:
                              done = True
                              break
              return done
  
          done = check_nexthop('10.0.6.0/24', q3)
          self.assertTrue(done)
  
          med300 = {'name': 'med300',
                    'type': 'permit',
                    'match': '0.0.0.0/0',
                    'med': 300,
                    'priority': 5}
          q3.add_policy(med300, self.gobgp, 'out')
  
          time.sleep(self.wait_per_retry)
  
          done = check_nexthop('10.0.6.0/24', q2)
          self.assertTrue(done)
  
  
  if __name__ == '__main__':
      if os.geteuid() is not 0:
          print "you are not root."
          sys.exit(1)
      output = local("which docker 2>&1 > /dev/null ; echo $?", capture=True)
      if int(output) is not 0:
          print "docker not found"
          sys.exit(1)
  
      nose.main(argv=sys.argv, addplugins=[OptionParser()],
                defaultTest=sys.argv[0])
  