summaryrefslogtreecommitdiffhomepage
path: root/test/scenario_test/lib/base.py
diff options
context:
space:
mode:
authorISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>2015-07-02 15:29:05 +0900
committerFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>2015-07-03 06:29:07 +0900
commit34c88c4771a5dd4fb687fe225cebfc6fc99381f0 (patch)
treeb6144f582473b2aea9127cbbcb113d1b16b57a77 /test/scenario_test/lib/base.py
parent64f5846bc6e1a12b0a9bf3fb8d56d33ebe46b811 (diff)
test: introduce modular test library
Signed-off-by: ISHIDA Wataru <ishida.wataru@lab.ntt.co.jp>
Diffstat (limited to 'test/scenario_test/lib/base.py')
-rw-r--r--test/scenario_test/lib/base.py266
1 files changed, 266 insertions, 0 deletions
diff --git a/test/scenario_test/lib/base.py b/test/scenario_test/lib/base.py
new file mode 100644
index 00000000..35686973
--- /dev/null
+++ b/test/scenario_test/lib/base.py
@@ -0,0 +1,266 @@
+# 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.
+
+from fabric.api import local, lcd
+from fabric import colors
+from fabric.utils import indent
+
+import netaddr
+import os
+import time
+import itertools
+
+DEFAULT_TEST_BASE_DIR = '/tmp/gobgp'
+TEST_BASE_DIR = DEFAULT_TEST_BASE_DIR
+
+BGP_FSM_IDLE = 'BGP_FSM_IDLE'
+BGP_FSM_ACTIVE = 'BGP_FSM_ACTIVE'
+BGP_FSM_ESTABLISHED = 'BGP_FSM_ESTABLISHED'
+
+
+def get_bridges():
+ return local("brctl show | awk 'NR > 1{print $1}'",
+ capture=True).split('\n')
+
+
+def get_containers():
+ return local("docker ps -a | awk 'NR > 1 {print $NF}'",
+ capture=True).split('\n')
+
+
+class CmdBuffer(list):
+ def __init__(self, delim='\n'):
+ super(CmdBuffer, self).__init__()
+ self.delim = delim
+
+ def __lshift__(self, value):
+ self.append(value)
+
+ def __str__(self):
+ return self.delim.join(self)
+
+
+def make_gobgp_ctn(tag='gobgp', local_gobgp_path=''):
+ if local_gobgp_path == '':
+ local_gobgp_path = os.getcwd()
+
+ c = CmdBuffer()
+ c << 'FROM osrg/gobgp'
+ c << 'COPY gobgp /go/src/github.com/osrg/gobgp/'
+ c << 'RUN go get github.com/osrg/gobgp/gobgpd'
+ c << 'RUN go install -a github.com/osrg/gobgp/gobgpd'
+ c << 'RUN go get github.com/osrg/gobgp/gobgp'
+ c << 'RUN go install -a github.com/osrg/gobgp/gobgp'
+
+ rindex = local_gobgp_path.rindex('gobgp')
+ if rindex < 0:
+ raise Exception('{0} seems not gobgp dir'.format(local_gobgp_path))
+
+ workdir = local_gobgp_path[:rindex]
+ with lcd(workdir):
+ local('echo \'{0}\' > Dockerfile'.format(str(c)))
+ local('docker build -t {0} .'.format(tag))
+ local('rm Dockerfile')
+
+
+class Bridge(object):
+ def __init__(self, name, subnet='', with_ip=True):
+ self.name = name
+ self.with_ip = with_ip
+ if with_ip:
+ self.subnet = netaddr.IPNetwork(subnet)
+
+ def f():
+ for host in self.subnet:
+ yield host
+ self._ip_generator = f()
+ # throw away first network address
+ self.next_ip_address()
+
+ if self.name in get_bridges():
+ self.delete()
+
+ local("ip link add {0} type bridge".format(self.name), capture=True)
+ local("ip link set up dev {0}".format(self.name), capture=True)
+
+ if with_ip:
+ self.ip_addr = self.next_ip_address()
+ local("ip addr add {0} dev {1}".format(self.ip_addr, self.name),
+ capture=True)
+
+ self.ctns = []
+
+ def next_ip_address(self):
+ return "{0}/{1}".format(self._ip_generator.next(),
+ self.subnet.prefixlen)
+
+ def addif(self, ctn, name=''):
+ if name == '':
+ name = self.name
+ self.ctns.append(ctn)
+ if self.with_ip:
+ ctn.pipework(self, self.next_ip_address(), name)
+ else:
+ ctn.pipework(self, '0/0', name)
+
+ def delete(self):
+ local("ip link set down dev {0}".format(self.name), capture=True)
+ local("ip link delete {0} type bridge".format(self.name), capture=True)
+
+
+class Container(object):
+ def __init__(self, name, image):
+ self.name = name
+ self.image = image
+ self.shared_volumes = []
+ self.ip_addrs = []
+ self.is_running = False
+
+ if self.name in get_containers():
+ self.stop()
+
+ def run(self):
+ c = CmdBuffer(' ')
+ c << "docker run --privileged=true"
+ for sv in self.shared_volumes:
+ c << "-v {0}:{1}".format(sv[0], sv[1])
+ c << "--name {0} -id {1}".format(self.name, self.image)
+ self.id = local(str(c), capture=True)
+ self.is_running = True
+ self.local("ip li set up dev lo")
+ return 0
+
+ def stop(self):
+ ret = local("docker rm -f " + self.name, capture=True)
+ self.is_running = False
+ return ret
+
+ def pipework(self, bridge, ip_addr, intf_name=""):
+ if not self.is_running:
+ print colors.yellow('call run() before pipeworking')
+ return
+ c = CmdBuffer(' ')
+ c << "pipework {0}".format(bridge.name)
+
+ if intf_name != "":
+ c << "-i {0}".format(intf_name)
+ else:
+ intf_name = "eth1"
+ c << "{0} {1}".format(self.name, ip_addr)
+ self.ip_addrs.append((intf_name, ip_addr, bridge))
+ return local(str(c), capture=True)
+
+ def local(self, cmd):
+ return local("docker exec -it {0} {1}".format(self.name, cmd))
+
+
+class BGPContainer(Container):
+
+ WAIT_FOR_BOOT = 0
+ RETRY_INTERVAL = 5
+
+ def __init__(self, name, asn, router_id, ctn_image_name):
+ self.config_dir = "{0}/{1}".format(TEST_BASE_DIR, name)
+ local('if [ -e {0} ]; then rm -r {0}; fi'.format(self.config_dir))
+ local('mkdir -p {0}'.format(self.config_dir))
+ self.asn = asn
+ self.router_id = router_id
+ self.peers = {}
+ self.routes = {}
+ self.policies = {}
+ super(BGPContainer, self).__init__(name, ctn_image_name)
+
+ def run(self):
+ self.create_config()
+ super(BGPContainer, self).run()
+ return self.WAIT_FOR_BOOT
+
+ def add_peer(self, peer, passwd='', evpn=False, is_rs_client=False,
+ policies=None, passive=False,
+ is_rr_client=False, cluster_id=''):
+ neigh_addr = ''
+ for me, you in itertools.product(self.ip_addrs, peer.ip_addrs):
+ if me[2] == you[2]:
+ neigh_addr = you[1]
+
+ if neigh_addr == '':
+ raise Exception('peer {0} seems not ip reachable'.format(peer))
+
+ if not policies:
+ policies = []
+
+ self.peers[peer] = {'neigh_addr': neigh_addr,
+ 'passwd': passwd,
+ 'evpn': evpn,
+ 'is_rs_client': is_rs_client,
+ 'is_rr_client': is_rr_client,
+ 'cluster_id': cluster_id,
+ 'policies': policies,
+ 'passive': passive}
+ if self.is_running:
+ self.create_config()
+ self.reload_config()
+
+ def del_peer(self, peer):
+ del self.peers[peer]
+ if self.is_running:
+ self.create_config()
+ self.reload_config()
+
+ def add_route(self, route, attribute=''):
+ self.routes[route] = attribute
+ if self.is_running:
+ self.create_config()
+ self.reload_config()
+
+ def add_policy(self, policy, peer=None):
+ self.policies[policy['name']] = policy
+ if peer in self.peers:
+ self.peers[peer]['policies'].append(policy)
+ if self.is_running:
+ self.create_config()
+ self.reload_config()
+
+ def get_local_rib(self, peer, rf):
+ raise Exception('implement get_local_rib() method')
+
+ def get_global_rib(self, rf):
+ raise Exception('implement get_global_rib() method')
+
+ def get_neighbor_state(self, peer_id):
+ raise Exception('implement get_neighbor() method')
+
+ def wait_for(self, expected_state, peer, timeout=10):
+ interval = 1
+ count = 0
+ while True:
+ state = self.get_neighbor_state(peer)
+ y = colors.yellow
+ print y("{0}'s peer {1} state: {2}".format(self.router_id,
+ peer.router_id,
+ state))
+ if state == expected_state:
+ return
+
+ time.sleep(interval)
+ count += interval
+ if count >= timeout:
+ raise Exception('timeout')
+
+ def create_config(self):
+ raise Exception('implement create_config() method')
+
+ def reload_config(self):
+ raise Exception('implement reload_config() method')