diff options
author | IWASE Yusuke <iwase.yusuke0@gmail.com> | 2018-01-25 14:42:33 +0900 |
---|---|---|
committer | FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp> | 2018-01-27 20:48:42 +0900 |
commit | 5ea3cbdd9dc41e6dcf3c0258455a357e154efdb1 (patch) | |
tree | 6f331a98b035cb5d027ee556de6958064e19c25c /test | |
parent | 38223f2f512cce3d05c9ae8f29ade983aa723cef (diff) |
test/lib/Quagga: Use vtysh to add/del routes
Currently, to advertise or withdraw routes on QuaggaBGPContainer, we
need to configure static routes in configure file and restart Quagga
daemons.
In other words, we can NOT send withdrawing advertisement from Quagga
side.
Also, restating Quagga daemons frequently can make scenario test
unstable, and it should be avoided.
This patch fixes to use "vtysh" and reduces the number of restating
Quagga daemons.
Note: According to this change, adding routes on Quagga should be called
after starting daemons.
Signed-off-by: IWASE Yusuke <iwase.yusuke0@gmail.com>
Diffstat (limited to 'test')
-rw-r--r-- | test/lib/quagga.py | 150 | ||||
-rw-r--r-- | test/scenario_test/bgp_router_test.py | 20 | ||||
-rw-r--r-- | test/scenario_test/ibgp_router_test.py | 25 | ||||
-rw-r--r-- | test/scenario_test/route_reflector_test.py | 11 | ||||
-rw-r--r-- | test/scenario_test/route_server_ipv4_v6_test.py | 17 | ||||
-rw-r--r-- | test/scenario_test/route_server_test.py | 23 |
6 files changed, 177 insertions, 69 deletions
diff --git a/test/lib/quagga.py b/test/lib/quagga.py index e657b439..0b5ecdd1 100644 --- a/test/lib/quagga.py +++ b/test/lib/quagga.py @@ -14,11 +14,11 @@ # limitations under the License. from __future__ import absolute_import + import re from fabric import colors from fabric.utils import indent -from itertools import chain import netaddr from lib.base import ( @@ -63,13 +63,16 @@ class QuaggaBGPContainer(BGPContainer): daemons.append('zebra') return daemons - def _wait_for_boot(self): - for daemon in self._get_enabled_daemons(): - def _f(): - ret = self.local("vtysh -d {0} -c 'show run' > /dev/null 2>&1; echo $?".format(daemon), capture=True) - return ret == '0' + def _is_running(self): + def f(d): + return self.local( + 'vtysh -d {0} -c "show version"' + ' > /dev/null 2>&1; echo $?'.format(d), capture=True) == '0' - wait_for_completion(_f) + return all([f(d) for d in self._get_enabled_daemons()]) + + def _wait_for_boot(self): + wait_for_completion(self._is_running) def run(self): super(QuaggaBGPContainer, self).run() @@ -215,16 +218,6 @@ class QuaggaBGPContainer(BGPContainer): c << 'neighbor {0} activate'.format(n_addr) c << 'exit-address-family' - for route in chain.from_iterable(self.routes.itervalues()): - if route['rf'] == 'ipv4': - c << 'network {0}'.format(route['prefix']) - elif route['rf'] == 'ipv6': - c << 'address-family ipv6 unicast' - c << 'network {0}'.format(route['prefix']) - c << 'exit-address-family' - else: - raise Exception('unsupported route faily: {0}'.format(route['rf'])) - if self.zebra: if version == 6: c << 'address-family ipv6 unicast' @@ -281,6 +274,129 @@ class QuaggaBGPContainer(BGPContainer): self.local('pkill {0} -SIGHUP'.format(daemon), capture=True) self._wait_for_boot() + def _vtysh_add_route_map(self, path): + supported_attributes = ( + 'next-hop', + 'as-path', + 'community', + 'med', + 'local-pref', + 'extended-community', + ) + if not any([path[k] for k in supported_attributes]): + return '' + + c = CmdBuffer(' ') + route_map_name = 'RM-{0}'.format(path['prefix']) + c << "vtysh -c 'configure terminal'" + c << "-c 'route-map {0} permit 10'".format(route_map_name) + if path['next-hop']: + if path['rf'] == 'ipv4': + c << "-c 'set ip next-hop {0}'".format(path['next-hop']) + elif path['rf'] == 'ipv6': + c << "-c 'set ipv6 next-hop {0}'".format(path['next-hop']) + else: + raise ValueError('Unsupported address family: {0}'.format(path['rf'])) + if path['as-path']: + as_path = ' '.join([str(n) for n in path['as-path']]) + c << "-c 'set as-path prepend {0}'".format(as_path) + if path['community']: + comm = ' '.join(path['community']) + c << "-c 'set community {0}'".format(comm) + if path['med']: + c << "-c 'set metric {0}'".format(path['med']) + if path['local-pref']: + c << "-c 'set local-preference {0}'".format(path['local-pref']) + if path['extended-community']: + # Note: Currently only RT is supported. + extcomm = ' '.join(path['extended-community']) + c << "-c 'set extcommunity rt {0}'".format(extcomm) + self.local(str(c), capture=True) + + return route_map_name + + def add_route(self, route, rf='ipv4', attribute=None, aspath=None, + community=None, med=None, extendedcommunity=None, + nexthop=None, matchs=None, thens=None, + local_pref=None, identifier=None, reload_config=False): + if not self._is_running(): + raise RuntimeError('Quagga/Zebra is not yet running') + + if rf not in ('ipv4', 'ipv6'): + raise ValueError('Unsupported address family: {0}'.format(rf)) + + self.routes.setdefault(route, []) + path = { + 'prefix': route, + 'rf': rf, + 'next-hop': nexthop, + 'as-path': aspath, + 'community': community, + 'med': med, + 'local-pref': local_pref, + 'extended-community': extendedcommunity, + # Note: The following settings are not yet supported on this + # implementation. + 'attr': None, + 'identifier': None, + 'matchs': None, + 'thens': None, + } + + # Prepare route-map before adding prefix + route_map_name = self._vtysh_add_route_map(path) + path['route_map'] = route_map_name + + c = CmdBuffer(' ') + c << "vtysh -c 'configure terminal'" + c << "-c 'router bgp {0}'".format(self.asn) + if rf == 'ipv6': + c << "-c 'address-family ipv6'" + if route_map_name: + c << "-c 'network {0} route-map {1}'".format(route, route_map_name) + else: + c << "-c 'network {0}'".format(route) + self.local(str(c), capture=True) + + self.routes[route].append(path) + + def _vtysh_del_route_map(self, path): + route_map_name = path.get('route_map', '') + if not route_map_name: + return + + c = CmdBuffer(' ') + c << "vtysh -c 'configure terminal'" + c << "-c 'no route-map {0}'".format(route_map_name) + self.local(str(c), capture=True) + + def del_route(self, route, identifier=None, reload_config=False): + if not self._is_running(): + raise RuntimeError('Quagga/Zebra is not yet running') + + path = None + new_paths = [] + for p in self.routes.get(route, []): + if p['identifier'] != identifier: + new_paths.append(p) + else: + path = p + if not path: + return + + rf = path['rf'] + c = CmdBuffer(' ') + c << "vtysh -c 'configure terminal'" + c << "-c 'router bgp {0}'".format(self.asn) + c << "-c 'address-family {0} unicast'".format(rf) + c << "-c 'no network {0}'".format(route) + self.local(str(c), capture=True) + + # Delete route-map after deleting prefix + self._vtysh_del_route_map(path) + + self.routes[route] = new_paths + class RawQuaggaBGPContainer(QuaggaBGPContainer): def __init__(self, name, config, ctn_image_name='osrg/quagga', zebra=False): diff --git a/test/scenario_test/bgp_router_test.py b/test/scenario_test/bgp_router_test.py index 1a92a1b3..7d93ba53 100644 --- a/test/scenario_test/bgp_router_test.py +++ b/test/scenario_test/bgp_router_test.py @@ -59,21 +59,17 @@ class GoBGPTestBase(unittest.TestCase): qs = [q1, q2, q3] ctns = [g1, q1, q2, q3] - # advertise a route from q1, q2, q3 - for idx, q in enumerate(qs): - route = '10.0.{0}.0/24'.format(idx + 1) - q.add_route(route) - initial_wait_time = max(ctn.run() for ctn in ctns) - time.sleep(initial_wait_time) for q in qs: - g1.add_peer(q, reload_config=False, passwd='passwd') + g1.add_peer(q, passwd='passwd') q.add_peer(g1, passwd='passwd', passive=True) - g1.create_config() - g1.reload_config() + # advertise a route from q1, q2, q3 + for idx, q in enumerate(qs): + route = '10.0.{0}.0/24'.format(idx + 1) + q.add_route(route) cls.gobgp = g1 cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3} @@ -145,14 +141,14 @@ class GoBGPTestBase(unittest.TestCase): def test_05_add_quagga(self): q4 = QuaggaBGPContainer(name='q4', asn=65004, router_id='192.168.0.5') self.quaggas['q4'] = q4 - - q4.add_route('10.0.4.0/24') - initial_wait_time = q4.run() time.sleep(initial_wait_time) + self.gobgp.add_peer(q4) q4.add_peer(self.gobgp) + q4.add_route('10.0.4.0/24') + self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q4) def test_06_check_global_rib(self): diff --git a/test/scenario_test/ibgp_router_test.py b/test/scenario_test/ibgp_router_test.py index ed476122..59815e6e 100644 --- a/test/scenario_test/ibgp_router_test.py +++ b/test/scenario_test/ibgp_router_test.py @@ -51,13 +51,7 @@ class GoBGPTestBase(unittest.TestCase): qs = [q1, q2] ctns = [g1, q1, q2] - # advertise a route from q1, q2 - for idx, c in enumerate(qs): - route = '10.0.{0}.0/24'.format(idx + 1) - c.add_route(route) - initial_wait_time = max(ctn.run() for ctn in ctns) - time.sleep(initial_wait_time) # ibgp peer. loop topology @@ -65,6 +59,11 @@ class GoBGPTestBase(unittest.TestCase): a.add_peer(b) b.add_peer(a) + # advertise a route from q1, q2 + for idx, c in enumerate(qs): + route = '10.0.{0}.0/24'.format(idx + 1) + c.add_route(route) + cls.gobgp = g1 cls.quaggas = {'q1': q1, 'q2': q2} @@ -163,14 +162,14 @@ class GoBGPTestBase(unittest.TestCase): def test_07_add_ebgp_peer(self): q3 = QuaggaBGPContainer(name='q3', asn=65001, router_id='192.168.0.4') self.quaggas['q3'] = q3 - - q3.add_route('10.0.3.0/24') - initial_wait_time = q3.run() time.sleep(initial_wait_time) + self.gobgp.add_peer(q3) q3.add_peer(self.gobgp) + q3.add_route('10.0.3.0/24') + self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q3) def test_08_check_global_rib(self): @@ -246,15 +245,15 @@ class GoBGPTestBase(unittest.TestCase): def test_15_add_ebgp_peer(self): q4 = QuaggaBGPContainer(name='q4', asn=65001, router_id='192.168.0.5') self.quaggas['q4'] = q4 - - prefix = '10.0.4.0/24' - q4.add_route(prefix) - initial_wait_time = q4.run() time.sleep(initial_wait_time) + self.gobgp.add_peer(q4) q4.add_peer(self.gobgp) + prefix = '10.0.4.0/24' + q4.add_route(prefix) + self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q4) q1 = self.quaggas['q1'] diff --git a/test/scenario_test/route_reflector_test.py b/test/scenario_test/route_reflector_test.py index a5516dec..29846d53 100644 --- a/test/scenario_test/route_reflector_test.py +++ b/test/scenario_test/route_reflector_test.py @@ -61,13 +61,7 @@ class GoBGPTestBase(unittest.TestCase): qs = [q1, q2, q3, q4] ctns = [g1, q1, q2, q3, q4] - # advertise a route from q1, q2 - for idx, c in enumerate(qs): - route = '10.0.{0}.0/24'.format(idx + 1) - c.add_route(route) - initial_wait_time = max(ctn.run() for ctn in ctns) - time.sleep(initial_wait_time) # g1 as a route reflector @@ -80,6 +74,11 @@ class GoBGPTestBase(unittest.TestCase): g1.add_peer(q4) q4.add_peer(g1) + # advertise a route from q1, q2 + for idx, c in enumerate(qs): + route = '10.0.{0}.0/24'.format(idx + 1) + c.add_route(route) + cls.gobgp = g1 cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3, 'q4': q4} diff --git a/test/scenario_test/route_server_ipv4_v6_test.py b/test/scenario_test/route_server_ipv4_v6_test.py index 9d7e15a7..07f77e4a 100644 --- a/test/scenario_test/route_server_ipv4_v6_test.py +++ b/test/scenario_test/route_server_ipv4_v6_test.py @@ -52,16 +52,7 @@ class GoBGPIPv6Test(unittest.TestCase): v4 = [q1, q2] v6 = [q3, q4] - for idx, q in enumerate(v4): - route = '10.0.{0}.0/24'.format(idx + 1) - q.add_route(route) - - for idx, q in enumerate(v6): - route = '2001:{0}::/96'.format(idx + 1) - q.add_route(route, rf='ipv6') - initial_wait_time = max(ctn.run() for ctn in ctns) - time.sleep(initial_wait_time) for ctn in v4: @@ -72,6 +63,14 @@ class GoBGPIPv6Test(unittest.TestCase): g1.add_peer(ctn, is_rs_client=True, v6=True) ctn.add_peer(g1, v6=True) + for idx, q in enumerate(v4): + route = '10.0.{0}.0/24'.format(idx + 1) + q.add_route(route) + + for idx, q in enumerate(v6): + route = '2001:{0}::/96'.format(idx + 1) + q.add_route(route, rf='ipv6') + cls.gobgp = g1 cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3, 'q4': q4} cls.ipv4s = {'q1': q1, 'q2': q2} diff --git a/test/scenario_test/route_server_test.py b/test/scenario_test/route_server_test.py index a99f1f8b..149adfd2 100644 --- a/test/scenario_test/route_server_test.py +++ b/test/scenario_test/route_server_test.py @@ -56,21 +56,20 @@ class GoBGPTestBase(unittest.TestCase): 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') + # 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) + cls.gobgp = g1 cls.quaggas = {'q1': q1, 'q2': q2, 'q3': q3} @@ -148,15 +147,15 @@ class GoBGPTestBase(unittest.TestCase): 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) + route = '10.0.4.0/24' + q4.add_route(route) + self.gobgp.wait_for(expected_state=BGP_FSM_ESTABLISHED, peer=q4) # check advertised routes are stored in gobgp's local-rib |