fumihiko kakuma
2017-07-10 03:51:26 UTC
This patch requires the v2 patch of Iwase-San.
Signed-off-by: Fumihiko Kakuma <***@valinux.co.jp>
---
.travis.yml | 2 +-
ryu/lib/docker/__init__.py | 0
ryu/lib/docker/docker_base.py | 801 +++++++++++++++++++++
ryu/lib/docker/install_docker_test_pkg.sh | 43 ++
ryu/lib/docker/install_docker_test_pkg_common.sh | 39 +
.../docker/install_docker_test_pkg_for_travis.sh | 12 +
ryu/lib/docker/quagga.py | 332 +++++++++
ryu/lib/docker/ryubgp.py | 212 ++++++
ryu/tests/integrated/common/__init__.py | 0
ryu/tests/integrated/common/docker_base.py | 801 ---------------------
.../integrated/common/install_docker_test_pkg.sh | 43 --
.../common/install_docker_test_pkg_common.sh | 39 -
.../common/install_docker_test_pkg_for_travis.sh | 12 -
ryu/tests/integrated/common/quagga.py | 332 ---------
ryu/tests/integrated/common/ryubgp.py | 212 ------
tests/integrated/bgp/base.py | 6 +-
tests/integrated/bgp/base_ip6.py | 6 +-
tests/integrated/bgp/test_basic.py | 2 +-
tests/integrated/bgp/test_ip6_basic.py | 2 +-
19 files changed, 1448 insertions(+), 1448 deletions(-)
create mode 100644 ryu/lib/docker/__init__.py
create mode 100644 ryu/lib/docker/docker_base.py
create mode 100644 ryu/lib/docker/install_docker_test_pkg.sh
create mode 100644 ryu/lib/docker/install_docker_test_pkg_common.sh
create mode 100644 ryu/lib/docker/install_docker_test_pkg_for_travis.sh
create mode 100644 ryu/lib/docker/quagga.py
create mode 100644 ryu/lib/docker/ryubgp.py
delete mode 100644 ryu/tests/integrated/common/__init__.py
delete mode 100644 ryu/tests/integrated/common/docker_base.py
delete mode 100644 ryu/tests/integrated/common/install_docker_test_pkg.sh
delete mode 100644 ryu/tests/integrated/common/install_docker_test_pkg_common.sh
delete mode 100644 ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
delete mode 100644 ryu/tests/integrated/common/quagga.py
delete mode 100644 ryu/tests/integrated/common/ryubgp.py
diff --git a/.travis.yml b/.travis.yml
index 9e5474a..cd35aac 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@ sudo: required # Required to enable Docker service
install:
- pip install tox coveralls
- - bash ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
+ - bash ryu/lib/docker/install_docker_test_pkg_for_travis.sh
script:
- NOSE_VERBOSE=0 tox -e $TOX_ENV
diff --git a/ryu/lib/docker/__init__.py b/ryu/lib/docker/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ryu/lib/docker/docker_base.py b/ryu/lib/docker/docker_base.py
new file mode 100644
index 0000000..1ae2cc2
--- /dev/null
+++ b/ryu/lib/docker/docker_base.py
@@ -0,0 +1,801 @@
+# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
+#
+# This is based on the following
+# https://github.com/osrg/gobgp/test/lib/base.py
+#
+# 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 __future__ import absolute_import
+
+import itertools
+import logging
+import os
+import subprocess
+import time
+
+import netaddr
+import six
+
+LOG = logging.getLogger(__name__)
+
+DEFAULT_TEST_PREFIX = ''
+DEFAULT_TEST_BASE_DIR = '/tmp/ctn_docker/bgp'
+TEST_PREFIX = DEFAULT_TEST_PREFIX
+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'
+
+BGP_ATTR_TYPE_ORIGIN = 1
+BGP_ATTR_TYPE_AS_PATH = 2
+BGP_ATTR_TYPE_NEXT_HOP = 3
+BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4
+BGP_ATTR_TYPE_LOCAL_PREF = 5
+BGP_ATTR_TYPE_COMMUNITIES = 8
+BGP_ATTR_TYPE_ORIGINATOR_ID = 9
+BGP_ATTR_TYPE_CLUSTER_LIST = 10
+BGP_ATTR_TYPE_MP_REACH_NLRI = 14
+BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16
+
+BRIDGE_TYPE_DOCKER = 'docker'
+BRIDGE_TYPE_BRCTL = 'brctl'
+BRIDGE_TYPE_OVS = 'ovs'
+
+
+class CommandError(Exception):
+ def __init__(self, out):
+ super(CommandError, self).__init__()
+ self.out = out
+
+
+def try_several_times(f, t=3, s=1):
+ e = RuntimeError()
+ for _ in range(t):
+ try:
+ r = f()
+ except RuntimeError as e:
+ time.sleep(s)
+ else:
+ return r
+ raise e
+
+
+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)
+
+
+class CommandOut(str):
+
+ def __new__(cls, stdout, stderr, command, returncode, **kwargs):
+ stdout = stdout or ''
+ obj = super(CommandOut, cls).__new__(cls, stdout, **kwargs)
+ obj.stderr = stderr or ''
+ obj.command = command
+ obj.returncode = returncode
+ return obj
+
+
+class Command(object):
+
+ def _execute(self, cmd, capture=False, executable=None):
+ """Execute a command using subprocess.Popen()
+ :Parameters:
+ - out: stdout from subprocess.Popen()
+ out has some attributes.
+ out.returncode: returncode of subprocess.Popen()
+ out.stderr: stderr from subprocess.Popen()
+ """
+ if capture:
+ p_stdout = subprocess.PIPE
+ p_stderr = subprocess.PIPE
+ else:
+ p_stdout = None
+ p_stderr = None
+ pop = subprocess.Popen(cmd, shell=True, executable=executable,
+ stdout=p_stdout,
+ stderr=p_stderr)
+ __stdout, __stderr = pop.communicate()
+ _stdout = six.text_type(__stdout, 'utf-8')
+ _stderr = six.text_type(__stderr, 'utf-8')
+ out = CommandOut(_stdout, _stderr, cmd, pop.returncode)
+ return out
+
+ def execute(self, cmd, capture=True, try_times=1, interval=1):
+ out = None
+ for i in range(try_times):
+ out = self._execute(cmd, capture=capture)
+ LOG.info(out.command)
+ if out.returncode == 0:
+ return out
+ LOG.error("stdout: %s", out)
+ LOG.error("stderr: %s", out.stderr)
+ if i + 1 >= try_times:
+ break
+ time.sleep(interval)
+ raise CommandError(out)
+
+ def sudo(self, cmd, capture=True, try_times=1, interval=1):
+ cmd = 'sudo %s' % cmd
+ return self.execute(cmd, capture=capture,
+ try_times=try_times, interval=interval)
+
+
+class DockerImage(object):
+ def __init__(self, baseimage='ubuntu:16.04'):
+ self.baseimage = baseimage
+ self.cmd = Command()
+
+ def get_images(self):
+ out = self.cmd.sudo('sudo docker images')
+ images = []
+ for line in out.splitlines()[1:]:
+ images.append(line.split()[0])
+ return images
+
+ def exist(self, name):
+ return name in self.get_images()
+
+ def build(self, tagname, dockerfile_dir):
+ self.cmd.sudo(
+ "docker build -t {0} {1}".format(tagname, dockerfile_dir),
+ try_times=3)
+
+ def remove(self, tagname, check_exist=False):
+ if check_exist and not self.exist(tagname):
+ return tagname
+ self.cmd.sudo("docker rmi -f %s" % tagname, try_times=3)
+
+ def create_quagga(self, tagname='quagga', image=None, check_exist=False):
+ if check_exist and self.exist(tagname):
+ return tagname
+ workdir = os.path.join(TEST_BASE_DIR, tagname)
+ pkges = ' '.join([
+ 'telnet',
+ 'tcpdump',
+ 'quagga',
+ ])
+ if image:
+ use_image = image
+ else:
+ use_image = self.baseimage
+ c = CmdBuffer()
+ c << 'FROM %s' % use_image
+ c << 'RUN apt-get update'
+ c << 'RUN apt-get install -qy --no-install-recommends %s' % pkges
+ c << 'CMD /usr/lib/quagga/bgpd'
+
+ self.cmd.sudo('rm -rf %s' % workdir)
+ self.cmd.execute('mkdir -p %s' % workdir)
+ self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
+ self.build(tagname, workdir)
+ return tagname
+
+ def create_ryu(self, tagname='ryu', image=None, check_exist=False):
+ if check_exist and self.exist(tagname):
+ return tagname
+ workdir = os.path.join(TEST_BASE_DIR, tagname)
+ workdir_ctn = '/root/osrg/ryu'
+ pkges = ' '.join([
+ 'tcpdump',
+ 'iproute2',
+ ])
+ if image:
+ use_image = image
+ else:
+ use_image = self.baseimage
+ c = CmdBuffer()
+ c << 'FROM %s' % use_image
+ c << 'ADD ryu %s' % workdir_ctn
+ install = ' '.join([
+ 'RUN apt-get update',
+ '&& apt-get install -qy --no-install-recommends %s' % pkges,
+ '&& cd %s' % workdir_ctn,
+ # Note: Clean previous builds, because "python setup.py install"
+ # might fail if the current directory contains the symlink to
+ # Docker host file systems.
+ '&& rm -rf *.egg-info/ build/ dist/ .tox/ *.log'
+ '&& pip install -r tools/pip-requires -r tools/optional-requires',
+ '&& python setup.py install',
+ ])
+ c << install
+
+ self.cmd.sudo('rm -rf %s' % workdir)
+ self.cmd.execute('mkdir -p %s' % workdir)
+ self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
+ self.cmd.execute('cp -r ../ryu %s/' % workdir)
+ self.build(tagname, workdir)
+ return tagname
+
+
+class Bridge(object):
+ def __init__(self, name, subnet='', start_ip=None, end_ip=None,
+ with_ip=True, self_ip=False,
+ fixed_ip=None, reuse=False,
+ br_type='docker'):
+ """Manage a bridge
+ :Parameters:
+ - name: bridge name
+ - subnet: network cider to be used in this bridge
+ - start_ip: start address of an ip to be used in the subnet
+ - end_ip: end address of an ip to be used in the subnet
+ - with_ip: specify if assign automatically an ip address
+ - self_ip: specify if assign an ip address for the bridge
+ - fixed_ip: an ip address to be assigned to the bridge
+ - reuse: specify if use an existing bridge
+ - br_type: One either in a 'docker', 'brctl' or 'ovs'
+ """
+ self.cmd = Command()
+ self.name = name
+ if br_type not in (BRIDGE_TYPE_DOCKER, BRIDGE_TYPE_BRCTL,
+ BRIDGE_TYPE_OVS):
+ raise Exception("argument error br_type: %s" % br_type)
+ self.br_type = br_type
+ self.docker_nw = bool(self.br_type == BRIDGE_TYPE_DOCKER)
+ if TEST_PREFIX != '':
+ self.name = '{0}_{1}'.format(TEST_PREFIX, name)
+ self.with_ip = with_ip
+ if with_ip:
+ self.subnet = netaddr.IPNetwork(subnet)
+ if start_ip:
+ self.start_ip = start_ip
+ else:
+ self.start_ip = netaddr.IPAddress(self.subnet.first)
+ if end_ip:
+ self.end_ip = end_ip
+ else:
+ self.end_ip = netaddr.IPAddress(self.subnet.last)
+
+ def _ip_gen():
+ for host in netaddr.IPRange(self.start_ip, self.end_ip):
+ yield host
+ self._ip_generator = _ip_gen()
+ # throw away first network address
+ self.next_ip_address()
+
+ self.self_ip = self_ip
+ if fixed_ip:
+ self.ip_addr = fixed_ip
+ else:
+ self.ip_addr = self.next_ip_address()
+ if not reuse:
+ def f():
+ if self.br_type == BRIDGE_TYPE_DOCKER:
+ gw = "--gateway %s" % self.ip_addr.split('/')[0]
+ v6 = ''
+ if self.subnet.version == 6:
+ v6 = '--ipv6'
+ cmd = ("docker network create --driver bridge %s "
+ "%s --subnet %s %s" % (v6, gw, subnet, self.name))
+ elif self.br_type == BRIDGE_TYPE_BRCTL:
+ cmd = "ip link add {0} type bridge".format(self.name)
+ elif self.br_type == BRIDGE_TYPE_OVS:
+ cmd = "ovs-vsctl add-br {0}".format(self.name)
+ else:
+ raise ValueError('Unsupported br_type: %s' % self.br_type)
+ self.delete()
+ self.execute(cmd, sudo=True, retry=True)
+ try_several_times(f)
+ if not self.docker_nw:
+ self.execute("ip link set up dev {0}".format(self.name),
+ sudo=True, retry=True)
+
+ if not self.docker_nw and self_ip:
+ ips = self.check_br_addr(self.name)
+ for key, ip in ips.items():
+ if self.subnet.version == key:
+ self.execute(
+ "ip addr del {0} dev {1}".format(ip, self.name),
+ sudo=True, retry=True)
+ self.execute(
+ "ip addr add {0} dev {1}".format(self.ip_addr, self.name),
+ sudo=True, retry=True)
+ self.ctns = []
+
+ def get_bridges_dc(self):
+ out = self.execute('docker network ls', sudo=True, retry=True)
+ bridges = []
+ for line in out.splitlines()[1:]:
+ bridges.append(line.split()[1])
+ return bridges
+
+ def get_bridges_brctl(self):
+ out = self.execute('brctl show', retry=True)
+ bridges = []
+ for line in out.splitlines()[1:]:
+ bridges.append(line.split()[0])
+ return bridges
+
+ def get_bridges_ovs(self):
+ out = self.execute('ovs-vsctl list-br', sudo=True, retry=True)
+ return out.splitlines()
+
+ def get_bridges(self):
+ if self.br_type == BRIDGE_TYPE_DOCKER:
+ return self.get_bridges_dc()
+ elif self.br_type == BRIDGE_TYPE_BRCTL:
+ return self.get_bridges_brctl()
+ elif self.br_type == BRIDGE_TYPE_OVS:
+ return self.get_bridges_ovs()
+
+ def exist(self):
+ return self.name in self.get_bridges()
+
+ def execute(self, cmd, capture=True, sudo=False, retry=False):
+ if sudo:
+ m = self.cmd.sudo
+ else:
+ m = self.cmd.execute
+ if retry:
+ return m(cmd, capture=capture, try_times=3, interval=1)
+ else:
+ return m(cmd, capture=capture)
+
+ def check_br_addr(self, br):
+ ips = {}
+ cmd = "ip a show dev %s" % br
+ for line in self.execute(cmd, sudo=True).split('\n'):
+ if line.strip().startswith("inet "):
+ elems = [e.strip() for e in line.strip().split(' ')]
+ ips[4] = elems[1]
+ elif line.strip().startswith("inet6 "):
+ elems = [e.strip() for e in line.strip().split(' ')]
+ ips[6] = elems[1]
+ return ips
+
+ def next_ip_address(self):
+ return "{0}/{1}".format(next(self._ip_generator),
+ self.subnet.prefixlen)
+
+ def addif(self, ctn):
+ name = ctn.next_if_name()
+ self.ctns.append(ctn)
+ ip_address = None
+ if self.docker_nw:
+ ipv4 = None
+ ipv6 = None
+ ip_address = self.next_ip_address()
+ ip_address_ip = ip_address.split('/')[0]
+ version = 4
+ if netaddr.IPNetwork(ip_address).version == 6:
+ version = 6
+ opt_ip = "--ip %s" % ip_address_ip
+ if version == 4:
+ ipv4 = ip_address
+ else:
+ opt_ip = "--ip6 %s" % ip_address_ip
+ ipv6 = ip_address
+ cmd = "docker network connect %s %s %s" % (
+ opt_ip, self.name, ctn.docker_name())
+ self.execute(cmd, sudo=True)
+ ctn.set_addr_info(bridge=self.name, ipv4=ipv4, ipv6=ipv6,
+ ifname=name)
+ else:
+ if self.with_ip:
+ ip_address = self.next_ip_address()
+ version = 4
+ if netaddr.IPNetwork(ip_address).version == 6:
+ version = 6
+ ctn.pipework(self, ip_address, name, version=version)
+ else:
+ ctn.pipework(self, '0/0', name)
+ return ip_address
+
+ def delete(self, check_exist=True):
+ if check_exist:
+ if not self.exist():
+ return
+ if self.br_type == BRIDGE_TYPE_DOCKER:
+ self.execute("docker network rm %s" % self.name,
+ sudo=True, retry=True)
+ elif self.br_type == BRIDGE_TYPE_BRCTL:
+ self.execute("ip link set down dev %s" % self.name,
+ sudo=True, retry=True)
+ self.execute(
+ "ip link delete %s type bridge" % self.name,
+ sudo=True, retry=True)
+ elif self.br_type == BRIDGE_TYPE_OVS:
+ self.execute(
+ "ovs-vsctl del-br %s" % self.name,
+ sudo=True, retry=True)
+
+
+class Container(object):
+ def __init__(self, name, image=None):
+ self.name = name
+ self.image = image
+ self.shared_volumes = []
+ self.ip_addrs = []
+ self.ip6_addrs = []
+ self.is_running = False
+ self.eths = []
+ self.id = None
+
+ self.cmd = Command()
+ self.remove()
+
+ def docker_name(self):
+ if TEST_PREFIX == DEFAULT_TEST_PREFIX:
+ return self.name
+ return '{0}_{1}'.format(TEST_PREFIX, self.name)
+
+ def get_docker_id(self):
+ if self.id:
+ return self.id
+ else:
+ return self.docker_name()
+
+ def next_if_name(self):
+ name = 'eth{0}'.format(len(self.eths) + 1)
+ self.eths.append(name)
+ return name
+
+ def set_addr_info(self, bridge, ipv4=None, ipv6=None, ifname='eth0'):
+ if ipv4:
+ self.ip_addrs.append((ifname, ipv4, bridge))
+ if ipv6:
+ self.ip6_addrs.append((ifname, ipv6, bridge))
+
+ def get_addr_info(self, bridge, ipv=4):
+ addrinfo = {}
+ if ipv == 4:
+ ip_addrs = self.ip_addrs
+ elif ipv == 6:
+ ip_addrs = self.ip6_addrs
+ else:
+ return None
+ for addr in ip_addrs:
+ if addr[2] == bridge:
+ addrinfo[addr[1]] = addr[0]
+ return addrinfo
+
+ def execute(self, cmd, capture=True, sudo=False, retry=False):
+ if sudo:
+ m = self.cmd.sudo
+ else:
+ m = self.cmd.execute
+ if retry:
+ return m(cmd, capture=capture, try_times=3, interval=1)
+ else:
+ return m(cmd, capture=capture)
+
+ def dcexec(self, cmd, capture=True, retry=False):
+ if retry:
+ return self.cmd.sudo(cmd, capture=capture, try_times=3, interval=1)
+ else:
+ return self.cmd.sudo(cmd, capture=capture)
+
+ def exec_on_ctn(self, cmd, capture=True, detach=False):
+ name = self.docker_name()
+ flag = '-d' if detach else ''
+ return self.dcexec('docker exec {0} {1} {2}'.format(
+ flag, name, cmd), capture=capture)
+
+ def get_containers(self, allctn=False):
+ cmd = 'docker ps --no-trunc=true'
+ if allctn:
+ cmd += ' --all=true'
+ out = self.dcexec(cmd, retry=True)
+ containers = []
+ for line in out.splitlines()[1:]:
+ containers.append(line.split()[-1])
+ return containers
+
+ def exist(self, allctn=False):
+ return self.docker_name() in self.get_containers(allctn=allctn)
+
+ 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} --hostname {0} -id {1}".format(self.docker_name(),
+ self.image)
+ self.id = self.dcexec(str(c), retry=True)
+ self.is_running = True
+ self.exec_on_ctn("ip li set up dev lo")
+ ipv4 = None
+ ipv6 = None
+ for line in self.exec_on_ctn("ip a show dev eth0").split('\n'):
+ if line.strip().startswith("inet "):
+ elems = [e.strip() for e in line.strip().split(' ')]
+ ipv4 = elems[1]
+ elif line.strip().startswith("inet6 "):
+ elems = [e.strip() for e in line.strip().split(' ')]
+ ipv6 = elems[1]
+ self.set_addr_info(bridge='docker0', ipv4=ipv4, ipv6=ipv6,
+ ifname='eth0')
+ return 0
+
+ def stop(self, check_exist=True):
+ if check_exist:
+ if not self.exist(allctn=False):
+ return
+ ctn_id = self.get_docker_id()
+ out = self.dcexec('docker stop -t 0 %s' % ctn_id, retry=True)
+ self.is_running = False
+ return out
+
+ def remove(self, check_exist=True):
+ if check_exist:
+ if not self.exist(allctn=True):
+ return
+ ctn_id = self.get_docker_id()
+ out = self.dcexec('docker rm -f %s' % ctn_id, retry=True)
+ self.is_running = False
+ return out
+
+ def pipework(self, bridge, ip_addr, intf_name="", version=4):
+ if not self.is_running:
+ LOG.warning('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"
+ ipv4 = None
+ ipv6 = None
+ if version == 4:
+ ipv4 = ip_addr
+ else:
+ c << '-a 6'
+ ipv6 = ip_addr
+ c << "{0} {1}".format(self.docker_name(), ip_addr)
+ self.set_addr_info(bridge=bridge.name, ipv4=ipv4, ipv6=ipv6,
+ ifname=intf_name)
+ self.execute(str(c), sudo=True, retry=True)
+
+ def get_pid(self):
+ if self.is_running:
+ cmd = "docker inspect -f '{{.State.Pid}}' %s" % self.docker_name()
+ return int(self.dcexec(cmd))
+ return -1
+
+ def start_tcpdump(self, interface=None, filename=None):
+ if not interface:
+ interface = "eth0"
+ if not filename:
+ filename = "{0}/{1}.dump".format(
+ self.shared_volumes[0][1], interface)
+ self.exec_on_ctn(
+ "tcpdump -i {0} -w {1}".format(interface, filename),
+ detach=True)
+
+
+class BGPContainer(Container):
+
+ WAIT_FOR_BOOT = 1
+ RETRY_INTERVAL = 5
+ DEFAULT_PEER_ARG = {'neigh_addr': '',
+ 'passwd': None,
+ 'vpn': False,
+ 'flowspec': False,
+ 'is_rs_client': False,
+ 'is_rr_client': False,
+ 'cluster_id': None,
+ 'policies': None,
+ 'passive': False,
+ 'local_addr': '',
+ 'as2': False,
+ 'graceful_restart': None,
+ 'local_as': None,
+ 'prefix_limit': None}
+ default_peer_keys = sorted(DEFAULT_PEER_ARG.keys())
+ DEFAULT_ROUTE_ARG = {'prefix': None,
+ 'rf': 'ipv4',
+ 'attr': None,
+ 'next-hop': None,
+ 'as-path': None,
+ 'community': None,
+ 'med': None,
+ 'local-pref': None,
+ 'extended-community': None,
+ 'matchs': None,
+ 'thens': None}
+ default_route_keys = sorted(DEFAULT_ROUTE_ARG.keys())
+
+ def __init__(self, name, asn, router_id, ctn_image_name=None):
+ self.config_dir = TEST_BASE_DIR
+ if TEST_PREFIX:
+ self.config_dir = os.path.join(self.config_dir, TEST_PREFIX)
+ self.config_dir = os.path.join(self.config_dir, name)
+ self.asn = asn
+ self.router_id = router_id
+ self.peers = {}
+ self.routes = {}
+ self.policies = {}
+ super(BGPContainer, self).__init__(name, ctn_image_name)
+ self.execute(
+ 'rm -rf {0}'.format(self.config_dir), sudo=True)
+ self.execute('mkdir -p {0}'.format(self.config_dir))
+ self.execute('chmod 777 {0}'.format(self.config_dir))
+
+ def __repr__(self):
+ return str({'name': self.name, 'asn': self.asn,
+ 'router_id': self.router_id})
+
+ def run(self, wait=False, w_time=WAIT_FOR_BOOT):
+ self.create_config()
+ super(BGPContainer, self).run()
+ if wait:
+ time.sleep(w_time)
+ return w_time
+
+ def add_peer(self, peer, bridge='', reload_config=True, v6=False,
+ peer_info=None):
+ peer_info = peer_info or {}
+ self.peers[peer] = self.DEFAULT_PEER_ARG.copy()
+ self.peers[peer].update(peer_info)
+ peer_keys = sorted(self.peers[peer].keys())
+ if peer_keys != self.default_peer_keys:
+ raise Exception("argument error peer_info: %s" % peer_info)
+
+ neigh_addr = ''
+ local_addr = ''
+ it = itertools.product(self.ip_addrs, peer.ip_addrs)
+ if v6:
+ it = itertools.product(self.ip6_addrs, peer.ip6_addrs)
+
+ for me, you in it:
+ if bridge != '' and bridge != me[2]:
+ continue
+ if me[2] == you[2]:
+ neigh_addr = you[1]
+ local_addr = me[1]
+ if v6:
+ addr, mask = local_addr.split('/')
+ local_addr = "{0}%{1}/{2}".format(addr, me[0], mask)
+ break
+
+ if neigh_addr == '':
+ raise Exception('peer {0} seems not ip reachable'.format(peer))
+
+ if not self.peers[peer]['policies']:
+ self.peers[peer]['policies'] = {}
+
+ self.peers[peer]['neigh_addr'] = neigh_addr
+ self.peers[peer]['local_addr'] = local_addr
+ if self.is_running and reload_config:
+ self.create_config()
+ self.reload_config()
+
+ def del_peer(self, peer, reload_config=True):
+ del self.peers[peer]
+ if self.is_running and reload_config:
+ self.create_config()
+ self.reload_config()
+
+ def disable_peer(self, peer):
+ raise NotImplementedError()
+
+ def enable_peer(self, peer):
+ raise NotImplementedError()
+
+ def log(self):
+ return self.execute('cat {0}/*.log'.format(self.config_dir))
+
+ def add_route(self, route, reload_config=True, route_info=None):
+ route_info = route_info or {}
+ self.routes[route] = self.DEFAULT_ROUTE_ARG.copy()
+ self.routes[route].update(route_info)
+ route_keys = sorted(self.routes[route].keys())
+ if route_keys != self.default_route_keys:
+ raise Exception("argument error route_info: %s" % route_info)
+ self.routes[route]['prefix'] = route
+ if self.is_running and reload_config:
+ self.create_config()
+ self.reload_config()
+
+ def add_policy(self, policy, peer, typ, default='accept',
+ reload_config=True):
+ self.set_default_policy(peer, typ, default)
+ self.define_policy(policy)
+ self.assign_policy(peer, policy, typ)
+ if self.is_running and reload_config:
+ self.create_config()
+ self.reload_config()
+
+ def set_default_policy(self, peer, typ, default):
+ if (typ in ['in', 'out', 'import', 'export'] and
+ default in ['reject', 'accept']):
+ if 'default-policy' not in self.peers[peer]:
+ self.peers[peer]['default-policy'] = {}
+ self.peers[peer]['default-policy'][typ] = default
+ else:
+ raise Exception('wrong type or default')
+
+ def define_policy(self, policy):
+ self.policies[policy['name']] = policy
+
+ def assign_policy(self, peer, policy, typ):
+ if peer not in self.peers:
+ raise Exception('peer {0} not found'.format(peer.name))
+ name = policy['name']
+ if name not in self.policies:
+ raise Exception('policy {0} not found'.format(name))
+ self.peers[peer]['policies'][typ] = policy
+
+ def get_local_rib(self, peer, rf):
+ raise NotImplementedError()
+
+ def get_global_rib(self, rf):
+ raise NotImplementedError()
+
+ def get_neighbor_state(self, peer_id):
+ raise NotImplementedError()
+
+ def get_reachablily(self, prefix, timeout=20):
+ version = netaddr.IPNetwork(prefix).version
+ addr = prefix.split('/')[0]
+ if version == 4:
+ ping_cmd = 'ping'
+ elif version == 6:
+ ping_cmd = 'ping6'
+ else:
+ raise Exception(
+ 'unsupported route family: {0}'.format(version))
+ cmd = '/bin/bash -c "/bin/{0} -c 1 -w 1 {1} | xargs echo"'.format(
+ ping_cmd, addr)
+ interval = 1
+ count = 0
+ while True:
+ res = self.exec_on_ctn(cmd)
+ LOG.info(res)
+ if '1 packets received' in res and '0% packet loss':
+ break
+ time.sleep(interval)
+ count += interval
+ if count >= timeout:
+ raise Exception('timeout')
+ return True
+
+ def wait_for(self, expected_state, peer, timeout=120):
+ interval = 1
+ count = 0
+ while True:
+ state = self.get_neighbor_state(peer)
+ LOG.info("%s's peer %s state: %s",
+ self.router_id, peer.router_id, state)
+ if state == expected_state:
+ return
+
+ time.sleep(interval)
+ count += interval
+ if count >= timeout:
+ raise Exception('timeout')
+
+ def add_static_route(self, network, next_hop):
+ cmd = '/sbin/ip route add {0} via {1}'.format(network, next_hop)
+ self.exec_on_ctn(cmd)
+
+ def set_ipv6_forward(self):
+ cmd = 'sysctl -w net.ipv6.conf.all.forwarding=1'
+ self.exec_on_ctn(cmd)
+
+ def create_config(self):
+ raise NotImplementedError()
+
+ def reload_config(self):
+ raise NotImplementedError()
diff --git a/ryu/lib/docker/install_docker_test_pkg.sh b/ryu/lib/docker/install_docker_test_pkg.sh
new file mode 100644
index 0000000..a771dfc
--- /dev/null
+++ b/ryu/lib/docker/install_docker_test_pkg.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+set -ex
+
+RYU_PATH=`dirname $0`
+
+source ${RYU_PATH}/install_docker_test_pkg_common.sh
+
+function add_docker_aptline {
+ sudo apt-get update
+ if ! apt-cache search docker-engine | grep docker-engine; then
+ VER=`lsb_release -r`
+ if echo $VER | grep 12.04; then
+ REL_NAME=precise
+ elif echo $VER | grep 14.04; then
+ REL_NAME=trusty
+ elif echo $VER | grep 15.10; then
+ REL_NAME=wily
+ elif echo $VER | grep 16.04; then
+ REL_NAME=xenial
+ else
+ retrun 1
+ fi
+ RELEASE=ubuntu-$REL_NAME
+ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
+ sudo sh -c "echo deb https://apt.dockerproject.org/repo $RELEASE main > /etc/apt/sources.list.d/docker.list"
+ fi
+}
+
+init_variables
+process_options "$@"
+
+if [ $APTLINE_DOCKER -eq 1 ]; then
+ add_docker_aptline
+fi
+
+sudo apt-get update
+if apt-cache search docker-engine | grep docker-engine; then
+ DOCKER_PKG=docker-engine
+else
+ DOCKER_PKG=docker.io
+fi
+sudo apt-get install -y $DOCKER_PKG
+install_depends_pkg
diff --git a/ryu/lib/docker/install_docker_test_pkg_common.sh b/ryu/lib/docker/install_docker_test_pkg_common.sh
new file mode 100644
index 0000000..44a3e10
--- /dev/null
+++ b/ryu/lib/docker/install_docker_test_pkg_common.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+set -ex
+
+function init_variables {
+ APTLINE_DOCKER=0
+ DIR_BASE=/tmp
+}
+
+function process_options {
+ local max
+ local i
+ max=$#
+ i=1
+ while [ $i -le $max ]; do
+ case "$1" in
+ -a|--add-docker-aptline)
+ APTLINE_DOCKER=1
+ ;;
+ -d|--download-dir)
+ shift; ((i++))
+ DIR_BASE=$1
+ ;;
+ esac
+ shift; ((i++))
+ done
+}
+
+function install_pipework {
+ if ! which /usr/local/bin/pipework >/dev/null
+ then
+ sudo rm -rf $DIR_BASE/pipework
+ git clone https://github.com/jpetazzo/pipework.git $DIR_BASE/pipework
+ sudo install -m 0755 $DIR_BASE/pipework/pipework /usr/local/bin/pipework
+ fi
+}
+
+function install_depends_pkg {
+ install_pipework
+}
diff --git a/ryu/lib/docker/install_docker_test_pkg_for_travis.sh b/ryu/lib/docker/install_docker_test_pkg_for_travis.sh
new file mode 100644
index 0000000..d8c3b49
--- /dev/null
+++ b/ryu/lib/docker/install_docker_test_pkg_for_travis.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+set -ex
+
+RYU_PATH=`dirname $0`
+
+source ${RYU_PATH}/install_docker_test_pkg_common.sh
+
+init_variables
+process_options "$@"
+
+sudo apt-get update
+install_depends_pkg
diff --git a/ryu/lib/docker/quagga.py b/ryu/lib/docker/quagga.py
new file mode 100644
index 0000000..9b6d218
--- /dev/null
+++ b/ryu/lib/docker/quagga.py
@@ -0,0 +1,332 @@
+# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
+#
+# This is based on the following
+# https://github.com/osrg/gobgp/test/lib/quagga.py
+#
+# 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 __future__ import absolute_import
+
+import logging
+import os
+
+import netaddr
+
+from . import docker_base as base
+
+LOG = logging.getLogger(__name__)
+
+
+class QuaggaBGPContainer(base.BGPContainer):
+
+ WAIT_FOR_BOOT = 1
+ SHARED_VOLUME = '/etc/quagga'
+
+ def __init__(self, name, asn, router_id, ctn_image_name, zebra=False):
+ super(QuaggaBGPContainer, self).__init__(name, asn, router_id,
+ ctn_image_name)
+ self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
+ self.zebra = zebra
+ self._create_config_debian()
+
+ def run(self, wait=False, w_time=WAIT_FOR_BOOT):
+ w_time = super(QuaggaBGPContainer,
+ self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
+ return w_time
+
+ def get_global_rib(self, prefix='', rf='ipv4'):
+ rib = []
+ if prefix != '':
+ return self.get_global_rib_with_prefix(prefix, rf)
+
+ out = self.vtysh('show bgp {0} unicast'.format(rf), config=False)
+ if out.startswith('No BGP network exists'):
+ return rib
+
+ read_next = False
+
+ for line in out.split('\n'):
+ ibgp = False
+ if line[:2] == '*>':
+ line = line[2:]
+ if line[0] == 'i':
+ line = line[1:]
+ ibgp = True
+ elif not read_next:
+ continue
+
+ elems = line.split()
+
+ if len(elems) == 1:
+ read_next = True
+ prefix = elems[0]
+ continue
+ elif read_next:
+ nexthop = elems[0]
+ else:
+ prefix = elems[0]
+ nexthop = elems[1]
+ read_next = False
+
+ rib.append({'prefix': prefix, 'nexthop': nexthop,
+ 'ibgp': ibgp})
+
+ return rib
+
+ def get_global_rib_with_prefix(self, prefix, rf):
+ rib = []
+
+ lines = [line.strip() for line in self.vtysh(
+ 'show bgp {0} unicast {1}'.format(rf, prefix),
+ config=False).split('\n')]
+
+ if lines[0] == '% Network not in table':
+ return rib
+
+ lines = lines[2:]
+
+ if lines[0].startswith('Not advertised'):
+ lines.pop(0) # another useless line
+ elif lines[0].startswith('Advertised to non peer-group peers:'):
+ lines = lines[2:] # other useless lines
+ else:
+ raise Exception('unknown output format {0}'.format(lines))
+
+ if lines[0] == 'Local':
+ aspath = []
+ else:
+ aspath = [int(asn) for asn in lines[0].split()]
+
+ nexthop = lines[1].split()[0].strip()
+ info = [s.strip(',') for s in lines[2].split()]
+ attrs = []
+ if 'metric' in info:
+ med = info[info.index('metric') + 1]
+ attrs.append({'type': base.BGP_ATTR_TYPE_MULTI_EXIT_DISC,
+ 'metric': int(med)})
+ if 'localpref' in info:
+ localpref = info[info.index('localpref') + 1]
+ attrs.append({'type': base.BGP_ATTR_TYPE_LOCAL_PREF,
+ 'value': int(localpref)})
+
+ rib.append({'prefix': prefix, 'nexthop': nexthop,
+ 'aspath': aspath, 'attrs': attrs})
+
+ return rib
+
+ def get_neighbor_state(self, peer):
+ if peer not in self.peers:
+ raise Exception('not found peer {0}'.format(peer.router_id))
+
+ neigh_addr = self.peers[peer]['neigh_addr'].split('/')[0]
+
+ info = [l.strip() for l in self.vtysh(
+ 'show bgp neighbors {0}'.format(neigh_addr),
+ config=False).split('\n')]
+
+ if not info[0].startswith('BGP neighbor is'):
+ raise Exception('unknown format')
+
+ idx1 = info[0].index('BGP neighbor is ')
+ idx2 = info[0].index(',')
+ n_addr = info[0][idx1 + len('BGP neighbor is '):idx2]
+ if n_addr == neigh_addr:
+ idx1 = info[2].index('= ')
+ state = info[2][idx1 + len('= '):]
+ if state.startswith('Idle'):
+ return base.BGP_FSM_IDLE
+ elif state.startswith('Active'):
+ return base.BGP_FSM_ACTIVE
+ elif state.startswith('Established'):
+ return base.BGP_FSM_ESTABLISHED
+ else:
+ return state
+
+ raise Exception('not found peer {0}'.format(peer.router_id))
+
+ def send_route_refresh(self):
+ self.vtysh('clear ip bgp * soft', config=False)
+
+ def create_config(self):
+ zebra = 'no'
+ self._create_config_bgp()
+ if self.zebra:
+ zebra = 'yes'
+ self._create_config_zebra()
+ self._create_config_daemons(zebra)
+
+ def _create_config_debian(self):
+ c = base.CmdBuffer()
+ c << 'vtysh_enable=yes'
+ c << 'zebra_options=" --daemon -A 127.0.0.1"'
+ c << 'bgpd_options=" --daemon -A 127.0.0.1"'
+ c << 'ospfd_options=" --daemon -A 127.0.0.1"'
+ c << 'ospf6d_options=" --daemon -A ::1"'
+ c << 'ripd_options=" --daemon -A 127.0.0.1"'
+ c << 'ripngd_options=" --daemon -A ::1"'
+ c << 'isisd_options=" --daemon -A 127.0.0.1"'
+ c << 'babeld_options=" --daemon -A 127.0.0.1"'
+ c << 'watchquagga_enable=yes'
+ c << 'watchquagga_options=(--daemon)'
+ with open('{0}/debian.conf'.format(self.config_dir), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def _create_config_daemons(self, zebra='no'):
+ c = base.CmdBuffer()
+ c << 'zebra=%s' % zebra
+ c << 'bgpd=yes'
+ c << 'ospfd=no'
+ c << 'ospf6d=no'
+ c << 'ripd=no'
+ c << 'ripngd=no'
+ c << 'isisd=no'
+ c << 'babeld=no'
+ with open('{0}/daemons'.format(self.config_dir), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def _create_config_bgp(self):
+
+ c = base.CmdBuffer()
+ c << 'hostname bgpd'
+ c << 'password zebra'
+ c << 'router bgp {0}'.format(self.asn)
+ c << 'bgp router-id {0}'.format(self.router_id)
+ if any(info['graceful_restart'] for info in self.peers.values()):
+ c << 'bgp graceful-restart'
+
+ version = 4
+ for peer, info in self.peers.items():
+ version = netaddr.IPNetwork(info['neigh_addr']).version
+ n_addr = info['neigh_addr'].split('/')[0]
+ if version == 6:
+ c << 'no bgp default ipv4-unicast'
+
+ c << 'neighbor {0} remote-as {1}'.format(n_addr, peer.asn)
+ if info['is_rs_client']:
+ c << 'neighbor {0} route-server-client'.format(n_addr)
+ for typ, p in info['policies'].items():
+ c << 'neighbor {0} route-map {1} {2}'.format(n_addr, p['name'],
+ typ)
+ if info['passwd']:
+ c << 'neighbor {0} password {1}'.format(n_addr, info['passwd'])
+ if info['passive']:
+ c << 'neighbor {0} passive'.format(n_addr)
+ if version == 6:
+ c << 'address-family ipv6 unicast'
+ c << 'neighbor {0} activate'.format(n_addr)
+ c << 'exit-address-family'
+
+ for route in self.routes.values():
+ 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'
+ c << 'redistribute connected'
+ c << 'exit-address-family'
+ else:
+ c << 'redistribute connected'
+
+ for name, policy in self.policies.items():
+ c << 'access-list {0} {1} {2}'.format(name, policy['type'],
+ policy['match'])
+ c << 'route-map {0} permit 10'.format(name)
+ c << 'match ip address {0}'.format(name)
+ c << 'set metric {0}'.format(policy['med'])
+
+ c << 'debug bgp as4'
+ c << 'debug bgp fsm'
+ c << 'debug bgp updates'
+ c << 'debug bgp events'
+ c << 'log file {0}/bgpd.log'.format(self.SHARED_VOLUME)
+
+ with open('{0}/bgpd.conf'.format(self.config_dir), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def _create_config_zebra(self):
+ c = base.CmdBuffer()
+ c << 'hostname zebra'
+ c << 'password zebra'
+ c << 'log file {0}/zebra.log'.format(self.SHARED_VOLUME)
+ c << 'debug zebra packet'
+ c << 'debug zebra kernel'
+ c << 'debug zebra rib'
+ c << ''
+
+ with open('{0}/zebra.conf'.format(self.config_dir), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def vtysh(self, cmd, config=True):
+ if not isinstance(cmd, list):
+ cmd = [cmd]
+ cmd = ' '.join("-c '{0}'".format(c) for c in cmd)
+ if config:
+ return self.exec_on_ctn(
+ "vtysh -d bgpd -c 'en' -c 'conf t' -c "
+ "'router bgp {0}' {1}".format(self.asn, cmd),
+ capture=True)
+ else:
+ return self.exec_on_ctn("vtysh -d bgpd {0}".format(cmd),
+ capture=True)
+
+ def reload_config(self):
+ daemon = []
+ daemon.append('bgpd')
+ if self.zebra:
+ daemon.append('zebra')
+ for d in daemon:
+ cmd = '/usr/bin/pkill {0} -SIGHUP'.format(d)
+ self.exec_on_ctn(cmd, capture=True)
+
+
+class RawQuaggaBGPContainer(QuaggaBGPContainer):
+ def __init__(self, name, config, ctn_image_name,
+ zebra=False):
+ asn = None
+ router_id = None
+ for line in config.split('\n'):
+ line = line.strip()
+ if line.startswith('router bgp'):
+ asn = int(line[len('router bgp'):].strip())
+ if line.startswith('bgp router-id'):
+ router_id = line[len('bgp router-id'):].strip()
+ if not asn:
+ raise Exception('asn not in quagga config')
+ if not router_id:
+ raise Exception('router-id not in quagga config')
+ self.config = config
+ super(RawQuaggaBGPContainer, self).__init__(name, asn, router_id,
+ ctn_image_name, zebra)
+
+ def create_config(self):
+ with open(os.path.join(self.config_dir, 'bgpd.conf'), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(self.config)
+ f.writelines(self.config)
diff --git a/ryu/lib/docker/ryubgp.py b/ryu/lib/docker/ryubgp.py
new file mode 100644
index 0000000..8fe16f4
--- /dev/null
+++ b/ryu/lib/docker/ryubgp.py
@@ -0,0 +1,212 @@
+# Copyright (C) 2016 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 __future__ import absolute_import
+
+import logging
+import os
+import time
+
+from . import docker_base as base
+
+LOG = logging.getLogger(__name__)
+
+
+class RyuBGPContainer(base.BGPContainer):
+
+ WAIT_FOR_BOOT = 1
+ SHARED_VOLUME = '/etc/ryu'
+
+ def __init__(self, name, asn, router_id, ctn_image_name):
+ super(RyuBGPContainer, self).__init__(name, asn, router_id,
+ ctn_image_name)
+ self.RYU_CONF = os.path.join(self.config_dir, 'ryu.conf')
+ self.SHARED_RYU_CONF = os.path.join(self.SHARED_VOLUME, 'ryu.conf')
+ self.SHARED_BGP_CONF = os.path.join(self.SHARED_VOLUME, 'bgp_conf.py')
+ self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
+
+ def _create_config_ryu(self):
+ c = base.CmdBuffer()
+ c << '[DEFAULT]'
+ c << 'verbose=True'
+ c << 'log_file=/etc/ryu/manager.log'
+ with open(self.RYU_CONF, 'w') as f:
+ LOG.info("[%s's new config]" % self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def _create_config_ryu_bgp(self):
+ c = base.CmdBuffer()
+ c << 'import os'
+ c << ''
+ c << 'BGP = {'
+ c << " 'local_as': %s," % str(self.asn)
+ c << " 'router_id': '%s'," % self.router_id
+ c << " 'neighbors': ["
+ c << " {"
+ for peer, info in self.peers.items():
+ n_addr = info['neigh_addr'].split('/')[0]
+ c << " 'address': '%s'," % n_addr
+ c << " 'remote_as': %s," % str(peer.asn)
+ c << " 'enable_ipv4': True,"
+ c << " 'enable_ipv6': True,"
+ c << " 'enable_vpnv4': True,"
+ c << " 'enable_vpnv6': True,"
+ c << ' },'
+ c << ' ],'
+ c << " 'routes': ["
+ for route in self.routes.values():
+ c << " {"
+ c << " 'prefix': '%s'," % route['prefix']
+ c << " },"
+ c << " ],"
+ c << "}"
+ log_conf = """LOGGING = {
+
+ # We use python logging package for logging.
+ 'version': 1,
+ 'disable_existing_loggers': False,
+
+ 'formatters': {
+ 'verbose': {
+ 'format': '%(levelname)s %(asctime)s %(module)s ' +
+ '[%(process)d %(thread)d] %(message)s'
+ },
+ 'simple': {
+ 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)s ' +
+ '%(message)s'
+ },
+ 'stats': {
+ 'format': '%(message)s'
+ },
+ },
+
+ 'handlers': {
+ # Outputs log to console.
+ 'console': {
+ 'level': 'DEBUG',
+ 'class': 'logging.StreamHandler',
+ 'formatter': 'simple'
+ },
+ 'console_stats': {
+ 'level': 'DEBUG',
+ 'class': 'logging.StreamHandler',
+ 'formatter': 'stats'
+ },
+ # Rotates log file when its size reaches 10MB.
+ 'log_file': {
+ 'level': 'DEBUG',
+ 'class': 'logging.handlers.RotatingFileHandler',
+ 'filename': os.path.join('.', 'bgpspeaker.log'),
+ 'maxBytes': '10000000',
+ 'formatter': 'verbose'
+ },
+ 'stats_file': {
+ 'level': 'DEBUG',
+ 'class': 'logging.handlers.RotatingFileHandler',
+ 'filename': os.path.join('.', 'statistics_bgps.log'),
+ 'maxBytes': '10000000',
+ 'formatter': 'stats'
+ },
+ },
+
+ # Fine-grained control of logging per instance.
+ 'loggers': {
+ 'bgpspeaker': {
+ 'handlers': ['console', 'log_file'],
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'stats': {
+ 'handlers': ['stats_file', 'console_stats'],
+ 'level': 'INFO',
+ 'propagate': False,
+ 'formatter': 'stats',
+ },
+ },
+
+ # Root loggers.
+ 'root': {
+ 'handlers': ['console', 'log_file'],
+ 'level': 'DEBUG',
+ 'propagate': True,
+ },
+}"""
+ c << log_conf
+ with open(os.path.join(self.config_dir, 'bgp_conf.py'), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def create_config(self):
+ self._create_config_ryu()
+ self._create_config_ryu_bgp()
+
+ def is_running_ryu(self):
+ results = self.exec_on_ctn('ps ax')
+ running = False
+ for line in results.split('\n')[1:]:
+ if 'ryu-manager' in line:
+ running = True
+ return running
+
+ def start_ryubgp(self, check_running=True, retry=False):
+ if check_running:
+ if self.is_running_ryu():
+ return True
+ result = False
+ if retry:
+ try_times = 3
+ else:
+ try_times = 1
+ cmd = "ryu-manager --verbose "
+ cmd += "--config-file %s " % self.SHARED_RYU_CONF
+ cmd += "--bgp-app-config-file %s " % self.SHARED_BGP_CONF
+ cmd += "ryu.services.protocols.bgp.application"
+ for _ in range(try_times):
+ self.exec_on_ctn(cmd, detach=True)
+ if self.is_running_ryu():
+ result = True
+ break
+ time.sleep(1)
+ return result
+
+ def stop_ryubgp(self, check_running=True, retry=False):
+ if check_running:
+ if not self.is_running_ryu():
+ return True
+ result = False
+ if retry:
+ try_times = 3
+ else:
+ try_times = 1
+ for _ in range(try_times):
+ cmd = '/usr/bin/pkill ryu-manager -SIGTERM'
+ self.exec_on_ctn(cmd)
+ if not self.is_running_ryu():
+ result = True
+ break
+ time.sleep(1)
+ return result
+
+ def run(self, wait=False, w_time=WAIT_FOR_BOOT):
+ w_time = super(RyuBGPContainer,
+ self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
+ return w_time
+
+ def reload_config(self):
+ self.stop_ryubgp(retry=True)
+ self.start_ryubgp(retry=True)
diff --git a/ryu/tests/integrated/common/__init__.py b/ryu/tests/integrated/common/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/ryu/tests/integrated/common/docker_base.py b/ryu/tests/integrated/common/docker_base.py
deleted file mode 100644
index 1ae2cc2..0000000
--- a/ryu/tests/integrated/common/docker_base.py
+++ /dev/null
@@ -1,801 +0,0 @@
-# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
-#
-# This is based on the following
-# https://github.com/osrg/gobgp/test/lib/base.py
-#
-# 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 __future__ import absolute_import
-
-import itertools
-import logging
-import os
-import subprocess
-import time
-
-import netaddr
-import six
-
-LOG = logging.getLogger(__name__)
-
-DEFAULT_TEST_PREFIX = ''
-DEFAULT_TEST_BASE_DIR = '/tmp/ctn_docker/bgp'
-TEST_PREFIX = DEFAULT_TEST_PREFIX
-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'
-
-BGP_ATTR_TYPE_ORIGIN = 1
-BGP_ATTR_TYPE_AS_PATH = 2
-BGP_ATTR_TYPE_NEXT_HOP = 3
-BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4
-BGP_ATTR_TYPE_LOCAL_PREF = 5
-BGP_ATTR_TYPE_COMMUNITIES = 8
-BGP_ATTR_TYPE_ORIGINATOR_ID = 9
-BGP_ATTR_TYPE_CLUSTER_LIST = 10
-BGP_ATTR_TYPE_MP_REACH_NLRI = 14
-BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16
-
-BRIDGE_TYPE_DOCKER = 'docker'
-BRIDGE_TYPE_BRCTL = 'brctl'
-BRIDGE_TYPE_OVS = 'ovs'
-
-
-class CommandError(Exception):
- def __init__(self, out):
- super(CommandError, self).__init__()
- self.out = out
-
-
-def try_several_times(f, t=3, s=1):
- e = RuntimeError()
- for _ in range(t):
- try:
- r = f()
- except RuntimeError as e:
- time.sleep(s)
- else:
- return r
- raise e
-
-
-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)
-
-
-class CommandOut(str):
-
- def __new__(cls, stdout, stderr, command, returncode, **kwargs):
- stdout = stdout or ''
- obj = super(CommandOut, cls).__new__(cls, stdout, **kwargs)
- obj.stderr = stderr or ''
- obj.command = command
- obj.returncode = returncode
- return obj
-
-
-class Command(object):
-
- def _execute(self, cmd, capture=False, executable=None):
- """Execute a command using subprocess.Popen()
- :Parameters:
- - out: stdout from subprocess.Popen()
- out has some attributes.
- out.returncode: returncode of subprocess.Popen()
- out.stderr: stderr from subprocess.Popen()
- """
- if capture:
- p_stdout = subprocess.PIPE
- p_stderr = subprocess.PIPE
- else:
- p_stdout = None
- p_stderr = None
- pop = subprocess.Popen(cmd, shell=True, executable=executable,
- stdout=p_stdout,
- stderr=p_stderr)
- __stdout, __stderr = pop.communicate()
- _stdout = six.text_type(__stdout, 'utf-8')
- _stderr = six.text_type(__stderr, 'utf-8')
- out = CommandOut(_stdout, _stderr, cmd, pop.returncode)
- return out
-
- def execute(self, cmd, capture=True, try_times=1, interval=1):
- out = None
- for i in range(try_times):
- out = self._execute(cmd, capture=capture)
- LOG.info(out.command)
- if out.returncode == 0:
- return out
- LOG.error("stdout: %s", out)
- LOG.error("stderr: %s", out.stderr)
- if i + 1 >= try_times:
- break
- time.sleep(interval)
- raise CommandError(out)
-
- def sudo(self, cmd, capture=True, try_times=1, interval=1):
- cmd = 'sudo %s' % cmd
- return self.execute(cmd, capture=capture,
- try_times=try_times, interval=interval)
-
-
-class DockerImage(object):
- def __init__(self, baseimage='ubuntu:16.04'):
- self.baseimage = baseimage
- self.cmd = Command()
-
- def get_images(self):
- out = self.cmd.sudo('sudo docker images')
- images = []
- for line in out.splitlines()[1:]:
- images.append(line.split()[0])
- return images
-
- def exist(self, name):
- return name in self.get_images()
-
- def build(self, tagname, dockerfile_dir):
- self.cmd.sudo(
- "docker build -t {0} {1}".format(tagname, dockerfile_dir),
- try_times=3)
-
- def remove(self, tagname, check_exist=False):
- if check_exist and not self.exist(tagname):
- return tagname
- self.cmd.sudo("docker rmi -f %s" % tagname, try_times=3)
-
- def create_quagga(self, tagname='quagga', image=None, check_exist=False):
- if check_exist and self.exist(tagname):
- return tagname
- workdir = os.path.join(TEST_BASE_DIR, tagname)
- pkges = ' '.join([
- 'telnet',
- 'tcpdump',
- 'quagga',
- ])
- if image:
- use_image = image
- else:
- use_image = self.baseimage
- c = CmdBuffer()
- c << 'FROM %s' % use_image
- c << 'RUN apt-get update'
- c << 'RUN apt-get install -qy --no-install-recommends %s' % pkges
- c << 'CMD /usr/lib/quagga/bgpd'
-
- self.cmd.sudo('rm -rf %s' % workdir)
- self.cmd.execute('mkdir -p %s' % workdir)
- self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
- self.build(tagname, workdir)
- return tagname
-
- def create_ryu(self, tagname='ryu', image=None, check_exist=False):
- if check_exist and self.exist(tagname):
- return tagname
- workdir = os.path.join(TEST_BASE_DIR, tagname)
- workdir_ctn = '/root/osrg/ryu'
- pkges = ' '.join([
- 'tcpdump',
- 'iproute2',
- ])
- if image:
- use_image = image
- else:
- use_image = self.baseimage
- c = CmdBuffer()
- c << 'FROM %s' % use_image
- c << 'ADD ryu %s' % workdir_ctn
- install = ' '.join([
- 'RUN apt-get update',
- '&& apt-get install -qy --no-install-recommends %s' % pkges,
- '&& cd %s' % workdir_ctn,
- # Note: Clean previous builds, because "python setup.py install"
- # might fail if the current directory contains the symlink to
- # Docker host file systems.
- '&& rm -rf *.egg-info/ build/ dist/ .tox/ *.log'
- '&& pip install -r tools/pip-requires -r tools/optional-requires',
- '&& python setup.py install',
- ])
- c << install
-
- self.cmd.sudo('rm -rf %s' % workdir)
- self.cmd.execute('mkdir -p %s' % workdir)
- self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
- self.cmd.execute('cp -r ../ryu %s/' % workdir)
- self.build(tagname, workdir)
- return tagname
-
-
-class Bridge(object):
- def __init__(self, name, subnet='', start_ip=None, end_ip=None,
- with_ip=True, self_ip=False,
- fixed_ip=None, reuse=False,
- br_type='docker'):
- """Manage a bridge
- :Parameters:
- - name: bridge name
- - subnet: network cider to be used in this bridge
- - start_ip: start address of an ip to be used in the subnet
- - end_ip: end address of an ip to be used in the subnet
- - with_ip: specify if assign automatically an ip address
- - self_ip: specify if assign an ip address for the bridge
- - fixed_ip: an ip address to be assigned to the bridge
- - reuse: specify if use an existing bridge
- - br_type: One either in a 'docker', 'brctl' or 'ovs'
- """
- self.cmd = Command()
- self.name = name
- if br_type not in (BRIDGE_TYPE_DOCKER, BRIDGE_TYPE_BRCTL,
- BRIDGE_TYPE_OVS):
- raise Exception("argument error br_type: %s" % br_type)
- self.br_type = br_type
- self.docker_nw = bool(self.br_type == BRIDGE_TYPE_DOCKER)
- if TEST_PREFIX != '':
- self.name = '{0}_{1}'.format(TEST_PREFIX, name)
- self.with_ip = with_ip
- if with_ip:
- self.subnet = netaddr.IPNetwork(subnet)
- if start_ip:
- self.start_ip = start_ip
- else:
- self.start_ip = netaddr.IPAddress(self.subnet.first)
- if end_ip:
- self.end_ip = end_ip
- else:
- self.end_ip = netaddr.IPAddress(self.subnet.last)
-
- def _ip_gen():
- for host in netaddr.IPRange(self.start_ip, self.end_ip):
- yield host
- self._ip_generator = _ip_gen()
- # throw away first network address
- self.next_ip_address()
-
- self.self_ip = self_ip
- if fixed_ip:
- self.ip_addr = fixed_ip
- else:
- self.ip_addr = self.next_ip_address()
- if not reuse:
- def f():
- if self.br_type == BRIDGE_TYPE_DOCKER:
- gw = "--gateway %s" % self.ip_addr.split('/')[0]
- v6 = ''
- if self.subnet.version == 6:
- v6 = '--ipv6'
- cmd = ("docker network create --driver bridge %s "
- "%s --subnet %s %s" % (v6, gw, subnet, self.name))
- elif self.br_type == BRIDGE_TYPE_BRCTL:
- cmd = "ip link add {0} type bridge".format(self.name)
- elif self.br_type == BRIDGE_TYPE_OVS:
- cmd = "ovs-vsctl add-br {0}".format(self.name)
- else:
- raise ValueError('Unsupported br_type: %s' % self.br_type)
- self.delete()
- self.execute(cmd, sudo=True, retry=True)
- try_several_times(f)
- if not self.docker_nw:
- self.execute("ip link set up dev {0}".format(self.name),
- sudo=True, retry=True)
-
- if not self.docker_nw and self_ip:
- ips = self.check_br_addr(self.name)
- for key, ip in ips.items():
- if self.subnet.version == key:
- self.execute(
- "ip addr del {0} dev {1}".format(ip, self.name),
- sudo=True, retry=True)
- self.execute(
- "ip addr add {0} dev {1}".format(self.ip_addr, self.name),
- sudo=True, retry=True)
- self.ctns = []
-
- def get_bridges_dc(self):
- out = self.execute('docker network ls', sudo=True, retry=True)
- bridges = []
- for line in out.splitlines()[1:]:
- bridges.append(line.split()[1])
- return bridges
-
- def get_bridges_brctl(self):
- out = self.execute('brctl show', retry=True)
- bridges = []
- for line in out.splitlines()[1:]:
- bridges.append(line.split()[0])
- return bridges
-
- def get_bridges_ovs(self):
- out = self.execute('ovs-vsctl list-br', sudo=True, retry=True)
- return out.splitlines()
-
- def get_bridges(self):
- if self.br_type == BRIDGE_TYPE_DOCKER:
- return self.get_bridges_dc()
- elif self.br_type == BRIDGE_TYPE_BRCTL:
- return self.get_bridges_brctl()
- elif self.br_type == BRIDGE_TYPE_OVS:
- return self.get_bridges_ovs()
-
- def exist(self):
- return self.name in self.get_bridges()
-
- def execute(self, cmd, capture=True, sudo=False, retry=False):
- if sudo:
- m = self.cmd.sudo
- else:
- m = self.cmd.execute
- if retry:
- return m(cmd, capture=capture, try_times=3, interval=1)
- else:
- return m(cmd, capture=capture)
-
- def check_br_addr(self, br):
- ips = {}
- cmd = "ip a show dev %s" % br
- for line in self.execute(cmd, sudo=True).split('\n'):
- if line.strip().startswith("inet "):
- elems = [e.strip() for e in line.strip().split(' ')]
- ips[4] = elems[1]
- elif line.strip().startswith("inet6 "):
- elems = [e.strip() for e in line.strip().split(' ')]
- ips[6] = elems[1]
- return ips
-
- def next_ip_address(self):
- return "{0}/{1}".format(next(self._ip_generator),
- self.subnet.prefixlen)
-
- def addif(self, ctn):
- name = ctn.next_if_name()
- self.ctns.append(ctn)
- ip_address = None
- if self.docker_nw:
- ipv4 = None
- ipv6 = None
- ip_address = self.next_ip_address()
- ip_address_ip = ip_address.split('/')[0]
- version = 4
- if netaddr.IPNetwork(ip_address).version == 6:
- version = 6
- opt_ip = "--ip %s" % ip_address_ip
- if version == 4:
- ipv4 = ip_address
- else:
- opt_ip = "--ip6 %s" % ip_address_ip
- ipv6 = ip_address
- cmd = "docker network connect %s %s %s" % (
- opt_ip, self.name, ctn.docker_name())
- self.execute(cmd, sudo=True)
- ctn.set_addr_info(bridge=self.name, ipv4=ipv4, ipv6=ipv6,
- ifname=name)
- else:
- if self.with_ip:
- ip_address = self.next_ip_address()
- version = 4
- if netaddr.IPNetwork(ip_address).version == 6:
- version = 6
- ctn.pipework(self, ip_address, name, version=version)
- else:
- ctn.pipework(self, '0/0', name)
- return ip_address
-
- def delete(self, check_exist=True):
- if check_exist:
- if not self.exist():
- return
- if self.br_type == BRIDGE_TYPE_DOCKER:
- self.execute("docker network rm %s" % self.name,
- sudo=True, retry=True)
- elif self.br_type == BRIDGE_TYPE_BRCTL:
- self.execute("ip link set down dev %s" % self.name,
- sudo=True, retry=True)
- self.execute(
- "ip link delete %s type bridge" % self.name,
- sudo=True, retry=True)
- elif self.br_type == BRIDGE_TYPE_OVS:
- self.execute(
- "ovs-vsctl del-br %s" % self.name,
- sudo=True, retry=True)
-
-
-class Container(object):
- def __init__(self, name, image=None):
- self.name = name
- self.image = image
- self.shared_volumes = []
- self.ip_addrs = []
- self.ip6_addrs = []
- self.is_running = False
- self.eths = []
- self.id = None
-
- self.cmd = Command()
- self.remove()
-
- def docker_name(self):
- if TEST_PREFIX == DEFAULT_TEST_PREFIX:
- return self.name
- return '{0}_{1}'.format(TEST_PREFIX, self.name)
-
- def get_docker_id(self):
- if self.id:
- return self.id
- else:
- return self.docker_name()
-
- def next_if_name(self):
- name = 'eth{0}'.format(len(self.eths) + 1)
- self.eths.append(name)
- return name
-
- def set_addr_info(self, bridge, ipv4=None, ipv6=None, ifname='eth0'):
- if ipv4:
- self.ip_addrs.append((ifname, ipv4, bridge))
- if ipv6:
- self.ip6_addrs.append((ifname, ipv6, bridge))
-
- def get_addr_info(self, bridge, ipv=4):
- addrinfo = {}
- if ipv == 4:
- ip_addrs = self.ip_addrs
- elif ipv == 6:
- ip_addrs = self.ip6_addrs
- else:
- return None
- for addr in ip_addrs:
- if addr[2] == bridge:
- addrinfo[addr[1]] = addr[0]
- return addrinfo
-
- def execute(self, cmd, capture=True, sudo=False, retry=False):
- if sudo:
- m = self.cmd.sudo
- else:
- m = self.cmd.execute
- if retry:
- return m(cmd, capture=capture, try_times=3, interval=1)
- else:
- return m(cmd, capture=capture)
-
- def dcexec(self, cmd, capture=True, retry=False):
- if retry:
- return self.cmd.sudo(cmd, capture=capture, try_times=3, interval=1)
- else:
- return self.cmd.sudo(cmd, capture=capture)
-
- def exec_on_ctn(self, cmd, capture=True, detach=False):
- name = self.docker_name()
- flag = '-d' if detach else ''
- return self.dcexec('docker exec {0} {1} {2}'.format(
- flag, name, cmd), capture=capture)
-
- def get_containers(self, allctn=False):
- cmd = 'docker ps --no-trunc=true'
- if allctn:
- cmd += ' --all=true'
- out = self.dcexec(cmd, retry=True)
- containers = []
- for line in out.splitlines()[1:]:
- containers.append(line.split()[-1])
- return containers
-
- def exist(self, allctn=False):
- return self.docker_name() in self.get_containers(allctn=allctn)
-
- 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} --hostname {0} -id {1}".format(self.docker_name(),
- self.image)
- self.id = self.dcexec(str(c), retry=True)
- self.is_running = True
- self.exec_on_ctn("ip li set up dev lo")
- ipv4 = None
- ipv6 = None
- for line in self.exec_on_ctn("ip a show dev eth0").split('\n'):
- if line.strip().startswith("inet "):
- elems = [e.strip() for e in line.strip().split(' ')]
- ipv4 = elems[1]
- elif line.strip().startswith("inet6 "):
- elems = [e.strip() for e in line.strip().split(' ')]
- ipv6 = elems[1]
- self.set_addr_info(bridge='docker0', ipv4=ipv4, ipv6=ipv6,
- ifname='eth0')
- return 0
-
- def stop(self, check_exist=True):
- if check_exist:
- if not self.exist(allctn=False):
- return
- ctn_id = self.get_docker_id()
- out = self.dcexec('docker stop -t 0 %s' % ctn_id, retry=True)
- self.is_running = False
- return out
-
- def remove(self, check_exist=True):
- if check_exist:
- if not self.exist(allctn=True):
- return
- ctn_id = self.get_docker_id()
- out = self.dcexec('docker rm -f %s' % ctn_id, retry=True)
- self.is_running = False
- return out
-
- def pipework(self, bridge, ip_addr, intf_name="", version=4):
- if not self.is_running:
- LOG.warning('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"
- ipv4 = None
- ipv6 = None
- if version == 4:
- ipv4 = ip_addr
- else:
- c << '-a 6'
- ipv6 = ip_addr
- c << "{0} {1}".format(self.docker_name(), ip_addr)
- self.set_addr_info(bridge=bridge.name, ipv4=ipv4, ipv6=ipv6,
- ifname=intf_name)
- self.execute(str(c), sudo=True, retry=True)
-
- def get_pid(self):
- if self.is_running:
- cmd = "docker inspect -f '{{.State.Pid}}' %s" % self.docker_name()
- return int(self.dcexec(cmd))
- return -1
-
- def start_tcpdump(self, interface=None, filename=None):
- if not interface:
- interface = "eth0"
- if not filename:
- filename = "{0}/{1}.dump".format(
- self.shared_volumes[0][1], interface)
- self.exec_on_ctn(
- "tcpdump -i {0} -w {1}".format(interface, filename),
- detach=True)
-
-
-class BGPContainer(Container):
-
- WAIT_FOR_BOOT = 1
- RETRY_INTERVAL = 5
- DEFAULT_PEER_ARG = {'neigh_addr': '',
- 'passwd': None,
- 'vpn': False,
- 'flowspec': False,
- 'is_rs_client': False,
- 'is_rr_client': False,
- 'cluster_id': None,
- 'policies': None,
- 'passive': False,
- 'local_addr': '',
- 'as2': False,
- 'graceful_restart': None,
- 'local_as': None,
- 'prefix_limit': None}
- default_peer_keys = sorted(DEFAULT_PEER_ARG.keys())
- DEFAULT_ROUTE_ARG = {'prefix': None,
- 'rf': 'ipv4',
- 'attr': None,
- 'next-hop': None,
- 'as-path': None,
- 'community': None,
- 'med': None,
- 'local-pref': None,
- 'extended-community': None,
- 'matchs': None,
- 'thens': None}
- default_route_keys = sorted(DEFAULT_ROUTE_ARG.keys())
-
- def __init__(self, name, asn, router_id, ctn_image_name=None):
- self.config_dir = TEST_BASE_DIR
- if TEST_PREFIX:
- self.config_dir = os.path.join(self.config_dir, TEST_PREFIX)
- self.config_dir = os.path.join(self.config_dir, name)
- self.asn = asn
- self.router_id = router_id
- self.peers = {}
- self.routes = {}
- self.policies = {}
- super(BGPContainer, self).__init__(name, ctn_image_name)
- self.execute(
- 'rm -rf {0}'.format(self.config_dir), sudo=True)
- self.execute('mkdir -p {0}'.format(self.config_dir))
- self.execute('chmod 777 {0}'.format(self.config_dir))
-
- def __repr__(self):
- return str({'name': self.name, 'asn': self.asn,
- 'router_id': self.router_id})
-
- def run(self, wait=False, w_time=WAIT_FOR_BOOT):
- self.create_config()
- super(BGPContainer, self).run()
- if wait:
- time.sleep(w_time)
- return w_time
-
- def add_peer(self, peer, bridge='', reload_config=True, v6=False,
- peer_info=None):
- peer_info = peer_info or {}
- self.peers[peer] = self.DEFAULT_PEER_ARG.copy()
- self.peers[peer].update(peer_info)
- peer_keys = sorted(self.peers[peer].keys())
- if peer_keys != self.default_peer_keys:
- raise Exception("argument error peer_info: %s" % peer_info)
-
- neigh_addr = ''
- local_addr = ''
- it = itertools.product(self.ip_addrs, peer.ip_addrs)
- if v6:
- it = itertools.product(self.ip6_addrs, peer.ip6_addrs)
-
- for me, you in it:
- if bridge != '' and bridge != me[2]:
- continue
- if me[2] == you[2]:
- neigh_addr = you[1]
- local_addr = me[1]
- if v6:
- addr, mask = local_addr.split('/')
- local_addr = "{0}%{1}/{2}".format(addr, me[0], mask)
- break
-
- if neigh_addr == '':
- raise Exception('peer {0} seems not ip reachable'.format(peer))
-
- if not self.peers[peer]['policies']:
- self.peers[peer]['policies'] = {}
-
- self.peers[peer]['neigh_addr'] = neigh_addr
- self.peers[peer]['local_addr'] = local_addr
- if self.is_running and reload_config:
- self.create_config()
- self.reload_config()
-
- def del_peer(self, peer, reload_config=True):
- del self.peers[peer]
- if self.is_running and reload_config:
- self.create_config()
- self.reload_config()
-
- def disable_peer(self, peer):
- raise NotImplementedError()
-
- def enable_peer(self, peer):
- raise NotImplementedError()
-
- def log(self):
- return self.execute('cat {0}/*.log'.format(self.config_dir))
-
- def add_route(self, route, reload_config=True, route_info=None):
- route_info = route_info or {}
- self.routes[route] = self.DEFAULT_ROUTE_ARG.copy()
- self.routes[route].update(route_info)
- route_keys = sorted(self.routes[route].keys())
- if route_keys != self.default_route_keys:
- raise Exception("argument error route_info: %s" % route_info)
- self.routes[route]['prefix'] = route
- if self.is_running and reload_config:
- self.create_config()
- self.reload_config()
-
- def add_policy(self, policy, peer, typ, default='accept',
- reload_config=True):
- self.set_default_policy(peer, typ, default)
- self.define_policy(policy)
- self.assign_policy(peer, policy, typ)
- if self.is_running and reload_config:
- self.create_config()
- self.reload_config()
-
- def set_default_policy(self, peer, typ, default):
- if (typ in ['in', 'out', 'import', 'export'] and
- default in ['reject', 'accept']):
- if 'default-policy' not in self.peers[peer]:
- self.peers[peer]['default-policy'] = {}
- self.peers[peer]['default-policy'][typ] = default
- else:
- raise Exception('wrong type or default')
-
- def define_policy(self, policy):
- self.policies[policy['name']] = policy
-
- def assign_policy(self, peer, policy, typ):
- if peer not in self.peers:
- raise Exception('peer {0} not found'.format(peer.name))
- name = policy['name']
- if name not in self.policies:
- raise Exception('policy {0} not found'.format(name))
- self.peers[peer]['policies'][typ] = policy
-
- def get_local_rib(self, peer, rf):
- raise NotImplementedError()
-
- def get_global_rib(self, rf):
- raise NotImplementedError()
-
- def get_neighbor_state(self, peer_id):
- raise NotImplementedError()
-
- def get_reachablily(self, prefix, timeout=20):
- version = netaddr.IPNetwork(prefix).version
- addr = prefix.split('/')[0]
- if version == 4:
- ping_cmd = 'ping'
- elif version == 6:
- ping_cmd = 'ping6'
- else:
- raise Exception(
- 'unsupported route family: {0}'.format(version))
- cmd = '/bin/bash -c "/bin/{0} -c 1 -w 1 {1} | xargs echo"'.format(
- ping_cmd, addr)
- interval = 1
- count = 0
- while True:
- res = self.exec_on_ctn(cmd)
- LOG.info(res)
- if '1 packets received' in res and '0% packet loss':
- break
- time.sleep(interval)
- count += interval
- if count >= timeout:
- raise Exception('timeout')
- return True
-
- def wait_for(self, expected_state, peer, timeout=120):
- interval = 1
- count = 0
- while True:
- state = self.get_neighbor_state(peer)
- LOG.info("%s's peer %s state: %s",
- self.router_id, peer.router_id, state)
- if state == expected_state:
- return
-
- time.sleep(interval)
- count += interval
- if count >= timeout:
- raise Exception('timeout')
-
- def add_static_route(self, network, next_hop):
- cmd = '/sbin/ip route add {0} via {1}'.format(network, next_hop)
- self.exec_on_ctn(cmd)
-
- def set_ipv6_forward(self):
- cmd = 'sysctl -w net.ipv6.conf.all.forwarding=1'
- self.exec_on_ctn(cmd)
-
- def create_config(self):
- raise NotImplementedError()
-
- def reload_config(self):
- raise NotImplementedError()
diff --git a/ryu/tests/integrated/common/install_docker_test_pkg.sh b/ryu/tests/integrated/common/install_docker_test_pkg.sh
deleted file mode 100644
index a771dfc..0000000
--- a/ryu/tests/integrated/common/install_docker_test_pkg.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash
-set -ex
-
-RYU_PATH=`dirname $0`
-
-source ${RYU_PATH}/install_docker_test_pkg_common.sh
-
-function add_docker_aptline {
- sudo apt-get update
- if ! apt-cache search docker-engine | grep docker-engine; then
- VER=`lsb_release -r`
- if echo $VER | grep 12.04; then
- REL_NAME=precise
- elif echo $VER | grep 14.04; then
- REL_NAME=trusty
- elif echo $VER | grep 15.10; then
- REL_NAME=wily
- elif echo $VER | grep 16.04; then
- REL_NAME=xenial
- else
- retrun 1
- fi
- RELEASE=ubuntu-$REL_NAME
- sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
- sudo sh -c "echo deb https://apt.dockerproject.org/repo $RELEASE main > /etc/apt/sources.list.d/docker.list"
- fi
-}
-
-init_variables
-process_options "$@"
-
-if [ $APTLINE_DOCKER -eq 1 ]; then
- add_docker_aptline
-fi
-
-sudo apt-get update
-if apt-cache search docker-engine | grep docker-engine; then
- DOCKER_PKG=docker-engine
-else
- DOCKER_PKG=docker.io
-fi
-sudo apt-get install -y $DOCKER_PKG
-install_depends_pkg
diff --git a/ryu/tests/integrated/common/install_docker_test_pkg_common.sh b/ryu/tests/integrated/common/install_docker_test_pkg_common.sh
deleted file mode 100644
index 44a3e10..0000000
--- a/ryu/tests/integrated/common/install_docker_test_pkg_common.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-set -ex
-
-function init_variables {
- APTLINE_DOCKER=0
- DIR_BASE=/tmp
-}
-
-function process_options {
- local max
- local i
- max=$#
- i=1
- while [ $i -le $max ]; do
- case "$1" in
- -a|--add-docker-aptline)
- APTLINE_DOCKER=1
- ;;
- -d|--download-dir)
- shift; ((i++))
- DIR_BASE=$1
- ;;
- esac
- shift; ((i++))
- done
-}
-
-function install_pipework {
- if ! which /usr/local/bin/pipework >/dev/null
- then
- sudo rm -rf $DIR_BASE/pipework
- git clone https://github.com/jpetazzo/pipework.git $DIR_BASE/pipework
- sudo install -m 0755 $DIR_BASE/pipework/pipework /usr/local/bin/pipework
- fi
-}
-
-function install_depends_pkg {
- install_pipework
-}
diff --git a/ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh b/ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
deleted file mode 100644
index d8c3b49..0000000
--- a/ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-set -ex
-
-RYU_PATH=`dirname $0`
-
-source ${RYU_PATH}/install_docker_test_pkg_common.sh
-
-init_variables
-process_options "$@"
-
-sudo apt-get update
-install_depends_pkg
diff --git a/ryu/tests/integrated/common/quagga.py b/ryu/tests/integrated/common/quagga.py
deleted file mode 100644
index 9b6d218..0000000
--- a/ryu/tests/integrated/common/quagga.py
+++ /dev/null
@@ -1,332 +0,0 @@
-# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
-#
-# This is based on the following
-# https://github.com/osrg/gobgp/test/lib/quagga.py
-#
-# 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 __future__ import absolute_import
-
-import logging
-import os
-
-import netaddr
-
-from . import docker_base as base
-
-LOG = logging.getLogger(__name__)
-
-
-class QuaggaBGPContainer(base.BGPContainer):
-
- WAIT_FOR_BOOT = 1
- SHARED_VOLUME = '/etc/quagga'
-
- def __init__(self, name, asn, router_id, ctn_image_name, zebra=False):
- super(QuaggaBGPContainer, self).__init__(name, asn, router_id,
- ctn_image_name)
- self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
- self.zebra = zebra
- self._create_config_debian()
-
- def run(self, wait=False, w_time=WAIT_FOR_BOOT):
- w_time = super(QuaggaBGPContainer,
- self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
- return w_time
-
- def get_global_rib(self, prefix='', rf='ipv4'):
- rib = []
- if prefix != '':
- return self.get_global_rib_with_prefix(prefix, rf)
-
- out = self.vtysh('show bgp {0} unicast'.format(rf), config=False)
- if out.startswith('No BGP network exists'):
- return rib
-
- read_next = False
-
- for line in out.split('\n'):
- ibgp = False
- if line[:2] == '*>':
- line = line[2:]
- if line[0] == 'i':
- line = line[1:]
- ibgp = True
- elif not read_next:
- continue
-
- elems = line.split()
-
- if len(elems) == 1:
- read_next = True
- prefix = elems[0]
- continue
- elif read_next:
- nexthop = elems[0]
- else:
- prefix = elems[0]
- nexthop = elems[1]
- read_next = False
-
- rib.append({'prefix': prefix, 'nexthop': nexthop,
- 'ibgp': ibgp})
-
- return rib
-
- def get_global_rib_with_prefix(self, prefix, rf):
- rib = []
-
- lines = [line.strip() for line in self.vtysh(
- 'show bgp {0} unicast {1}'.format(rf, prefix),
- config=False).split('\n')]
-
- if lines[0] == '% Network not in table':
- return rib
-
- lines = lines[2:]
-
- if lines[0].startswith('Not advertised'):
- lines.pop(0) # another useless line
- elif lines[0].startswith('Advertised to non peer-group peers:'):
- lines = lines[2:] # other useless lines
- else:
- raise Exception('unknown output format {0}'.format(lines))
-
- if lines[0] == 'Local':
- aspath = []
- else:
- aspath = [int(asn) for asn in lines[0].split()]
-
- nexthop = lines[1].split()[0].strip()
- info = [s.strip(',') for s in lines[2].split()]
- attrs = []
- if 'metric' in info:
- med = info[info.index('metric') + 1]
- attrs.append({'type': base.BGP_ATTR_TYPE_MULTI_EXIT_DISC,
- 'metric': int(med)})
- if 'localpref' in info:
- localpref = info[info.index('localpref') + 1]
- attrs.append({'type': base.BGP_ATTR_TYPE_LOCAL_PREF,
- 'value': int(localpref)})
-
- rib.append({'prefix': prefix, 'nexthop': nexthop,
- 'aspath': aspath, 'attrs': attrs})
-
- return rib
-
- def get_neighbor_state(self, peer):
- if peer not in self.peers:
- raise Exception('not found peer {0}'.format(peer.router_id))
-
- neigh_addr = self.peers[peer]['neigh_addr'].split('/')[0]
-
- info = [l.strip() for l in self.vtysh(
- 'show bgp neighbors {0}'.format(neigh_addr),
- config=False).split('\n')]
-
- if not info[0].startswith('BGP neighbor is'):
- raise Exception('unknown format')
-
- idx1 = info[0].index('BGP neighbor is ')
- idx2 = info[0].index(',')
- n_addr = info[0][idx1 + len('BGP neighbor is '):idx2]
- if n_addr == neigh_addr:
- idx1 = info[2].index('= ')
- state = info[2][idx1 + len('= '):]
- if state.startswith('Idle'):
- return base.BGP_FSM_IDLE
- elif state.startswith('Active'):
- return base.BGP_FSM_ACTIVE
- elif state.startswith('Established'):
- return base.BGP_FSM_ESTABLISHED
- else:
- return state
-
- raise Exception('not found peer {0}'.format(peer.router_id))
-
- def send_route_refresh(self):
- self.vtysh('clear ip bgp * soft', config=False)
-
- def create_config(self):
- zebra = 'no'
- self._create_config_bgp()
- if self.zebra:
- zebra = 'yes'
- self._create_config_zebra()
- self._create_config_daemons(zebra)
-
- def _create_config_debian(self):
- c = base.CmdBuffer()
- c << 'vtysh_enable=yes'
- c << 'zebra_options=" --daemon -A 127.0.0.1"'
- c << 'bgpd_options=" --daemon -A 127.0.0.1"'
- c << 'ospfd_options=" --daemon -A 127.0.0.1"'
- c << 'ospf6d_options=" --daemon -A ::1"'
- c << 'ripd_options=" --daemon -A 127.0.0.1"'
- c << 'ripngd_options=" --daemon -A ::1"'
- c << 'isisd_options=" --daemon -A 127.0.0.1"'
- c << 'babeld_options=" --daemon -A 127.0.0.1"'
- c << 'watchquagga_enable=yes'
- c << 'watchquagga_options=(--daemon)'
- with open('{0}/debian.conf'.format(self.config_dir), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def _create_config_daemons(self, zebra='no'):
- c = base.CmdBuffer()
- c << 'zebra=%s' % zebra
- c << 'bgpd=yes'
- c << 'ospfd=no'
- c << 'ospf6d=no'
- c << 'ripd=no'
- c << 'ripngd=no'
- c << 'isisd=no'
- c << 'babeld=no'
- with open('{0}/daemons'.format(self.config_dir), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def _create_config_bgp(self):
-
- c = base.CmdBuffer()
- c << 'hostname bgpd'
- c << 'password zebra'
- c << 'router bgp {0}'.format(self.asn)
- c << 'bgp router-id {0}'.format(self.router_id)
- if any(info['graceful_restart'] for info in self.peers.values()):
- c << 'bgp graceful-restart'
-
- version = 4
- for peer, info in self.peers.items():
- version = netaddr.IPNetwork(info['neigh_addr']).version
- n_addr = info['neigh_addr'].split('/')[0]
- if version == 6:
- c << 'no bgp default ipv4-unicast'
-
- c << 'neighbor {0} remote-as {1}'.format(n_addr, peer.asn)
- if info['is_rs_client']:
- c << 'neighbor {0} route-server-client'.format(n_addr)
- for typ, p in info['policies'].items():
- c << 'neighbor {0} route-map {1} {2}'.format(n_addr, p['name'],
- typ)
- if info['passwd']:
- c << 'neighbor {0} password {1}'.format(n_addr, info['passwd'])
- if info['passive']:
- c << 'neighbor {0} passive'.format(n_addr)
- if version == 6:
- c << 'address-family ipv6 unicast'
- c << 'neighbor {0} activate'.format(n_addr)
- c << 'exit-address-family'
-
- for route in self.routes.values():
- 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'
- c << 'redistribute connected'
- c << 'exit-address-family'
- else:
- c << 'redistribute connected'
-
- for name, policy in self.policies.items():
- c << 'access-list {0} {1} {2}'.format(name, policy['type'],
- policy['match'])
- c << 'route-map {0} permit 10'.format(name)
- c << 'match ip address {0}'.format(name)
- c << 'set metric {0}'.format(policy['med'])
-
- c << 'debug bgp as4'
- c << 'debug bgp fsm'
- c << 'debug bgp updates'
- c << 'debug bgp events'
- c << 'log file {0}/bgpd.log'.format(self.SHARED_VOLUME)
-
- with open('{0}/bgpd.conf'.format(self.config_dir), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def _create_config_zebra(self):
- c = base.CmdBuffer()
- c << 'hostname zebra'
- c << 'password zebra'
- c << 'log file {0}/zebra.log'.format(self.SHARED_VOLUME)
- c << 'debug zebra packet'
- c << 'debug zebra kernel'
- c << 'debug zebra rib'
- c << ''
-
- with open('{0}/zebra.conf'.format(self.config_dir), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def vtysh(self, cmd, config=True):
- if not isinstance(cmd, list):
- cmd = [cmd]
- cmd = ' '.join("-c '{0}'".format(c) for c in cmd)
- if config:
- return self.exec_on_ctn(
- "vtysh -d bgpd -c 'en' -c 'conf t' -c "
- "'router bgp {0}' {1}".format(self.asn, cmd),
- capture=True)
- else:
- return self.exec_on_ctn("vtysh -d bgpd {0}".format(cmd),
- capture=True)
-
- def reload_config(self):
- daemon = []
- daemon.append('bgpd')
- if self.zebra:
- daemon.append('zebra')
- for d in daemon:
- cmd = '/usr/bin/pkill {0} -SIGHUP'.format(d)
- self.exec_on_ctn(cmd, capture=True)
-
-
-class RawQuaggaBGPContainer(QuaggaBGPContainer):
- def __init__(self, name, config, ctn_image_name,
- zebra=False):
- asn = None
- router_id = None
- for line in config.split('\n'):
- line = line.strip()
- if line.startswith('router bgp'):
- asn = int(line[len('router bgp'):].strip())
- if line.startswith('bgp router-id'):
- router_id = line[len('bgp router-id'):].strip()
- if not asn:
- raise Exception('asn not in quagga config')
- if not router_id:
- raise Exception('router-id not in quagga config')
- self.config = config
- super(RawQuaggaBGPContainer, self).__init__(name, asn, router_id,
- ctn_image_name, zebra)
-
- def create_config(self):
- with open(os.path.join(self.config_dir, 'bgpd.conf'), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(self.config)
- f.writelines(self.config)
diff --git a/ryu/tests/integrated/common/ryubgp.py b/ryu/tests/integrated/common/ryubgp.py
deleted file mode 100644
index 8fe16f4..0000000
--- a/ryu/tests/integrated/common/ryubgp.py
+++ /dev/null
@@ -1,212 +0,0 @@
-# Copyright (C) 2016 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 __future__ import absolute_import
-
-import logging
-import os
-import time
-
-from . import docker_base as base
-
-LOG = logging.getLogger(__name__)
-
-
-class RyuBGPContainer(base.BGPContainer):
-
- WAIT_FOR_BOOT = 1
- SHARED_VOLUME = '/etc/ryu'
-
- def __init__(self, name, asn, router_id, ctn_image_name):
- super(RyuBGPContainer, self).__init__(name, asn, router_id,
- ctn_image_name)
- self.RYU_CONF = os.path.join(self.config_dir, 'ryu.conf')
- self.SHARED_RYU_CONF = os.path.join(self.SHARED_VOLUME, 'ryu.conf')
- self.SHARED_BGP_CONF = os.path.join(self.SHARED_VOLUME, 'bgp_conf.py')
- self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
-
- def _create_config_ryu(self):
- c = base.CmdBuffer()
- c << '[DEFAULT]'
- c << 'verbose=True'
- c << 'log_file=/etc/ryu/manager.log'
- with open(self.RYU_CONF, 'w') as f:
- LOG.info("[%s's new config]" % self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def _create_config_ryu_bgp(self):
- c = base.CmdBuffer()
- c << 'import os'
- c << ''
- c << 'BGP = {'
- c << " 'local_as': %s," % str(self.asn)
- c << " 'router_id': '%s'," % self.router_id
- c << " 'neighbors': ["
- c << " {"
- for peer, info in self.peers.items():
- n_addr = info['neigh_addr'].split('/')[0]
- c << " 'address': '%s'," % n_addr
- c << " 'remote_as': %s," % str(peer.asn)
- c << " 'enable_ipv4': True,"
- c << " 'enable_ipv6': True,"
- c << " 'enable_vpnv4': True,"
- c << " 'enable_vpnv6': True,"
- c << ' },'
- c << ' ],'
- c << " 'routes': ["
- for route in self.routes.values():
- c << " {"
- c << " 'prefix': '%s'," % route['prefix']
- c << " },"
- c << " ],"
- c << "}"
- log_conf = """LOGGING = {
-
- # We use python logging package for logging.
- 'version': 1,
- 'disable_existing_loggers': False,
-
- 'formatters': {
- 'verbose': {
- 'format': '%(levelname)s %(asctime)s %(module)s ' +
- '[%(process)d %(thread)d] %(message)s'
- },
- 'simple': {
- 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)s ' +
- '%(message)s'
- },
- 'stats': {
- 'format': '%(message)s'
- },
- },
-
- 'handlers': {
- # Outputs log to console.
- 'console': {
- 'level': 'DEBUG',
- 'class': 'logging.StreamHandler',
- 'formatter': 'simple'
- },
- 'console_stats': {
- 'level': 'DEBUG',
- 'class': 'logging.StreamHandler',
- 'formatter': 'stats'
- },
- # Rotates log file when its size reaches 10MB.
- 'log_file': {
- 'level': 'DEBUG',
- 'class': 'logging.handlers.RotatingFileHandler',
- 'filename': os.path.join('.', 'bgpspeaker.log'),
- 'maxBytes': '10000000',
- 'formatter': 'verbose'
- },
- 'stats_file': {
- 'level': 'DEBUG',
- 'class': 'logging.handlers.RotatingFileHandler',
- 'filename': os.path.join('.', 'statistics_bgps.log'),
- 'maxBytes': '10000000',
- 'formatter': 'stats'
- },
- },
-
- # Fine-grained control of logging per instance.
- 'loggers': {
- 'bgpspeaker': {
- 'handlers': ['console', 'log_file'],
- 'handlers': ['console'],
- 'level': 'DEBUG',
- 'propagate': False,
- },
- 'stats': {
- 'handlers': ['stats_file', 'console_stats'],
- 'level': 'INFO',
- 'propagate': False,
- 'formatter': 'stats',
- },
- },
-
- # Root loggers.
- 'root': {
- 'handlers': ['console', 'log_file'],
- 'level': 'DEBUG',
- 'propagate': True,
- },
-}"""
- c << log_conf
- with open(os.path.join(self.config_dir, 'bgp_conf.py'), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def create_config(self):
- self._create_config_ryu()
- self._create_config_ryu_bgp()
-
- def is_running_ryu(self):
- results = self.exec_on_ctn('ps ax')
- running = False
- for line in results.split('\n')[1:]:
- if 'ryu-manager' in line:
- running = True
- return running
-
- def start_ryubgp(self, check_running=True, retry=False):
- if check_running:
- if self.is_running_ryu():
- return True
- result = False
- if retry:
- try_times = 3
- else:
- try_times = 1
- cmd = "ryu-manager --verbose "
- cmd += "--config-file %s " % self.SHARED_RYU_CONF
- cmd += "--bgp-app-config-file %s " % self.SHARED_BGP_CONF
- cmd += "ryu.services.protocols.bgp.application"
- for _ in range(try_times):
- self.exec_on_ctn(cmd, detach=True)
- if self.is_running_ryu():
- result = True
- break
- time.sleep(1)
- return result
-
- def stop_ryubgp(self, check_running=True, retry=False):
- if check_running:
- if not self.is_running_ryu():
- return True
- result = False
- if retry:
- try_times = 3
- else:
- try_times = 1
- for _ in range(try_times):
- cmd = '/usr/bin/pkill ryu-manager -SIGTERM'
- self.exec_on_ctn(cmd)
- if not self.is_running_ryu():
- result = True
- break
- time.sleep(1)
- return result
-
- def run(self, wait=False, w_time=WAIT_FOR_BOOT):
- w_time = super(RyuBGPContainer,
- self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
- return w_time
-
- def reload_config(self):
- self.stop_ryubgp(retry=True)
- self.start_ryubgp(retry=True)
diff --git a/tests/integrated/bgp/base.py b/tests/integrated/bgp/base.py
index 26fa396..2f210de 100644
--- a/tests/integrated/bgp/base.py
+++ b/tests/integrated/bgp/base.py
@@ -20,9 +20,9 @@ import logging
import sys
import unittest
-from ryu.tests.integrated.common import docker_base as ctn_base
-from ryu.tests.integrated.common import ryubgp
-from ryu.tests.integrated.common import quagga
+from ryu.lib.docker import docker_base as ctn_base
+from ryu.lib.docker import ryubgp
+from ryu.lib.docker import quagga
LOG = logging.getLogger(__name__)
diff --git a/tests/integrated/bgp/base_ip6.py b/tests/integrated/bgp/base_ip6.py
index be26faf..d867920 100644
--- a/tests/integrated/bgp/base_ip6.py
+++ b/tests/integrated/bgp/base_ip6.py
@@ -20,9 +20,9 @@ import logging
import sys
import unittest
-from ryu.tests.integrated.common import docker_base as ctn_base
-from ryu.tests.integrated.common import ryubgp
-from ryu.tests.integrated.common import quagga
+from ryu.lib.docker import docker_base as ctn_base
+from ryu.lib.docker import ryubgp
+from ryu.lib.docker import quagga
LOG = logging.getLogger(__name__)
diff --git a/tests/integrated/bgp/test_basic.py b/tests/integrated/bgp/test_basic.py
index 5817d44..d1eda39 100644
--- a/tests/integrated/bgp/test_basic.py
+++ b/tests/integrated/bgp/test_basic.py
@@ -18,7 +18,7 @@ from __future__ import absolute_import
import time
-from ryu.tests.integrated.common import docker_base as ctn_base
+from ryu.lib.docker import docker_base as ctn_base
from . import base
diff --git a/tests/integrated/bgp/test_ip6_basic.py b/tests/integrated/bgp/test_ip6_basic.py
index 40461a5..911a0b5 100644
--- a/tests/integrated/bgp/test_ip6_basic.py
+++ b/tests/integrated/bgp/test_ip6_basic.py
@@ -18,7 +18,7 @@ from __future__ import absolute_import
import time
-from ryu.tests.integrated.common import docker_base as ctn_base
+from ryu.lib.docker import docker_base as ctn_base
from . import base_ip6 as base
Signed-off-by: Fumihiko Kakuma <***@valinux.co.jp>
---
.travis.yml | 2 +-
ryu/lib/docker/__init__.py | 0
ryu/lib/docker/docker_base.py | 801 +++++++++++++++++++++
ryu/lib/docker/install_docker_test_pkg.sh | 43 ++
ryu/lib/docker/install_docker_test_pkg_common.sh | 39 +
.../docker/install_docker_test_pkg_for_travis.sh | 12 +
ryu/lib/docker/quagga.py | 332 +++++++++
ryu/lib/docker/ryubgp.py | 212 ++++++
ryu/tests/integrated/common/__init__.py | 0
ryu/tests/integrated/common/docker_base.py | 801 ---------------------
.../integrated/common/install_docker_test_pkg.sh | 43 --
.../common/install_docker_test_pkg_common.sh | 39 -
.../common/install_docker_test_pkg_for_travis.sh | 12 -
ryu/tests/integrated/common/quagga.py | 332 ---------
ryu/tests/integrated/common/ryubgp.py | 212 ------
tests/integrated/bgp/base.py | 6 +-
tests/integrated/bgp/base_ip6.py | 6 +-
tests/integrated/bgp/test_basic.py | 2 +-
tests/integrated/bgp/test_ip6_basic.py | 2 +-
19 files changed, 1448 insertions(+), 1448 deletions(-)
create mode 100644 ryu/lib/docker/__init__.py
create mode 100644 ryu/lib/docker/docker_base.py
create mode 100644 ryu/lib/docker/install_docker_test_pkg.sh
create mode 100644 ryu/lib/docker/install_docker_test_pkg_common.sh
create mode 100644 ryu/lib/docker/install_docker_test_pkg_for_travis.sh
create mode 100644 ryu/lib/docker/quagga.py
create mode 100644 ryu/lib/docker/ryubgp.py
delete mode 100644 ryu/tests/integrated/common/__init__.py
delete mode 100644 ryu/tests/integrated/common/docker_base.py
delete mode 100644 ryu/tests/integrated/common/install_docker_test_pkg.sh
delete mode 100644 ryu/tests/integrated/common/install_docker_test_pkg_common.sh
delete mode 100644 ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
delete mode 100644 ryu/tests/integrated/common/quagga.py
delete mode 100644 ryu/tests/integrated/common/ryubgp.py
diff --git a/.travis.yml b/.travis.yml
index 9e5474a..cd35aac 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,7 +16,7 @@ sudo: required # Required to enable Docker service
install:
- pip install tox coveralls
- - bash ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
+ - bash ryu/lib/docker/install_docker_test_pkg_for_travis.sh
script:
- NOSE_VERBOSE=0 tox -e $TOX_ENV
diff --git a/ryu/lib/docker/__init__.py b/ryu/lib/docker/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/ryu/lib/docker/docker_base.py b/ryu/lib/docker/docker_base.py
new file mode 100644
index 0000000..1ae2cc2
--- /dev/null
+++ b/ryu/lib/docker/docker_base.py
@@ -0,0 +1,801 @@
+# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
+#
+# This is based on the following
+# https://github.com/osrg/gobgp/test/lib/base.py
+#
+# 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 __future__ import absolute_import
+
+import itertools
+import logging
+import os
+import subprocess
+import time
+
+import netaddr
+import six
+
+LOG = logging.getLogger(__name__)
+
+DEFAULT_TEST_PREFIX = ''
+DEFAULT_TEST_BASE_DIR = '/tmp/ctn_docker/bgp'
+TEST_PREFIX = DEFAULT_TEST_PREFIX
+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'
+
+BGP_ATTR_TYPE_ORIGIN = 1
+BGP_ATTR_TYPE_AS_PATH = 2
+BGP_ATTR_TYPE_NEXT_HOP = 3
+BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4
+BGP_ATTR_TYPE_LOCAL_PREF = 5
+BGP_ATTR_TYPE_COMMUNITIES = 8
+BGP_ATTR_TYPE_ORIGINATOR_ID = 9
+BGP_ATTR_TYPE_CLUSTER_LIST = 10
+BGP_ATTR_TYPE_MP_REACH_NLRI = 14
+BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16
+
+BRIDGE_TYPE_DOCKER = 'docker'
+BRIDGE_TYPE_BRCTL = 'brctl'
+BRIDGE_TYPE_OVS = 'ovs'
+
+
+class CommandError(Exception):
+ def __init__(self, out):
+ super(CommandError, self).__init__()
+ self.out = out
+
+
+def try_several_times(f, t=3, s=1):
+ e = RuntimeError()
+ for _ in range(t):
+ try:
+ r = f()
+ except RuntimeError as e:
+ time.sleep(s)
+ else:
+ return r
+ raise e
+
+
+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)
+
+
+class CommandOut(str):
+
+ def __new__(cls, stdout, stderr, command, returncode, **kwargs):
+ stdout = stdout or ''
+ obj = super(CommandOut, cls).__new__(cls, stdout, **kwargs)
+ obj.stderr = stderr or ''
+ obj.command = command
+ obj.returncode = returncode
+ return obj
+
+
+class Command(object):
+
+ def _execute(self, cmd, capture=False, executable=None):
+ """Execute a command using subprocess.Popen()
+ :Parameters:
+ - out: stdout from subprocess.Popen()
+ out has some attributes.
+ out.returncode: returncode of subprocess.Popen()
+ out.stderr: stderr from subprocess.Popen()
+ """
+ if capture:
+ p_stdout = subprocess.PIPE
+ p_stderr = subprocess.PIPE
+ else:
+ p_stdout = None
+ p_stderr = None
+ pop = subprocess.Popen(cmd, shell=True, executable=executable,
+ stdout=p_stdout,
+ stderr=p_stderr)
+ __stdout, __stderr = pop.communicate()
+ _stdout = six.text_type(__stdout, 'utf-8')
+ _stderr = six.text_type(__stderr, 'utf-8')
+ out = CommandOut(_stdout, _stderr, cmd, pop.returncode)
+ return out
+
+ def execute(self, cmd, capture=True, try_times=1, interval=1):
+ out = None
+ for i in range(try_times):
+ out = self._execute(cmd, capture=capture)
+ LOG.info(out.command)
+ if out.returncode == 0:
+ return out
+ LOG.error("stdout: %s", out)
+ LOG.error("stderr: %s", out.stderr)
+ if i + 1 >= try_times:
+ break
+ time.sleep(interval)
+ raise CommandError(out)
+
+ def sudo(self, cmd, capture=True, try_times=1, interval=1):
+ cmd = 'sudo %s' % cmd
+ return self.execute(cmd, capture=capture,
+ try_times=try_times, interval=interval)
+
+
+class DockerImage(object):
+ def __init__(self, baseimage='ubuntu:16.04'):
+ self.baseimage = baseimage
+ self.cmd = Command()
+
+ def get_images(self):
+ out = self.cmd.sudo('sudo docker images')
+ images = []
+ for line in out.splitlines()[1:]:
+ images.append(line.split()[0])
+ return images
+
+ def exist(self, name):
+ return name in self.get_images()
+
+ def build(self, tagname, dockerfile_dir):
+ self.cmd.sudo(
+ "docker build -t {0} {1}".format(tagname, dockerfile_dir),
+ try_times=3)
+
+ def remove(self, tagname, check_exist=False):
+ if check_exist and not self.exist(tagname):
+ return tagname
+ self.cmd.sudo("docker rmi -f %s" % tagname, try_times=3)
+
+ def create_quagga(self, tagname='quagga', image=None, check_exist=False):
+ if check_exist and self.exist(tagname):
+ return tagname
+ workdir = os.path.join(TEST_BASE_DIR, tagname)
+ pkges = ' '.join([
+ 'telnet',
+ 'tcpdump',
+ 'quagga',
+ ])
+ if image:
+ use_image = image
+ else:
+ use_image = self.baseimage
+ c = CmdBuffer()
+ c << 'FROM %s' % use_image
+ c << 'RUN apt-get update'
+ c << 'RUN apt-get install -qy --no-install-recommends %s' % pkges
+ c << 'CMD /usr/lib/quagga/bgpd'
+
+ self.cmd.sudo('rm -rf %s' % workdir)
+ self.cmd.execute('mkdir -p %s' % workdir)
+ self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
+ self.build(tagname, workdir)
+ return tagname
+
+ def create_ryu(self, tagname='ryu', image=None, check_exist=False):
+ if check_exist and self.exist(tagname):
+ return tagname
+ workdir = os.path.join(TEST_BASE_DIR, tagname)
+ workdir_ctn = '/root/osrg/ryu'
+ pkges = ' '.join([
+ 'tcpdump',
+ 'iproute2',
+ ])
+ if image:
+ use_image = image
+ else:
+ use_image = self.baseimage
+ c = CmdBuffer()
+ c << 'FROM %s' % use_image
+ c << 'ADD ryu %s' % workdir_ctn
+ install = ' '.join([
+ 'RUN apt-get update',
+ '&& apt-get install -qy --no-install-recommends %s' % pkges,
+ '&& cd %s' % workdir_ctn,
+ # Note: Clean previous builds, because "python setup.py install"
+ # might fail if the current directory contains the symlink to
+ # Docker host file systems.
+ '&& rm -rf *.egg-info/ build/ dist/ .tox/ *.log'
+ '&& pip install -r tools/pip-requires -r tools/optional-requires',
+ '&& python setup.py install',
+ ])
+ c << install
+
+ self.cmd.sudo('rm -rf %s' % workdir)
+ self.cmd.execute('mkdir -p %s' % workdir)
+ self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
+ self.cmd.execute('cp -r ../ryu %s/' % workdir)
+ self.build(tagname, workdir)
+ return tagname
+
+
+class Bridge(object):
+ def __init__(self, name, subnet='', start_ip=None, end_ip=None,
+ with_ip=True, self_ip=False,
+ fixed_ip=None, reuse=False,
+ br_type='docker'):
+ """Manage a bridge
+ :Parameters:
+ - name: bridge name
+ - subnet: network cider to be used in this bridge
+ - start_ip: start address of an ip to be used in the subnet
+ - end_ip: end address of an ip to be used in the subnet
+ - with_ip: specify if assign automatically an ip address
+ - self_ip: specify if assign an ip address for the bridge
+ - fixed_ip: an ip address to be assigned to the bridge
+ - reuse: specify if use an existing bridge
+ - br_type: One either in a 'docker', 'brctl' or 'ovs'
+ """
+ self.cmd = Command()
+ self.name = name
+ if br_type not in (BRIDGE_TYPE_DOCKER, BRIDGE_TYPE_BRCTL,
+ BRIDGE_TYPE_OVS):
+ raise Exception("argument error br_type: %s" % br_type)
+ self.br_type = br_type
+ self.docker_nw = bool(self.br_type == BRIDGE_TYPE_DOCKER)
+ if TEST_PREFIX != '':
+ self.name = '{0}_{1}'.format(TEST_PREFIX, name)
+ self.with_ip = with_ip
+ if with_ip:
+ self.subnet = netaddr.IPNetwork(subnet)
+ if start_ip:
+ self.start_ip = start_ip
+ else:
+ self.start_ip = netaddr.IPAddress(self.subnet.first)
+ if end_ip:
+ self.end_ip = end_ip
+ else:
+ self.end_ip = netaddr.IPAddress(self.subnet.last)
+
+ def _ip_gen():
+ for host in netaddr.IPRange(self.start_ip, self.end_ip):
+ yield host
+ self._ip_generator = _ip_gen()
+ # throw away first network address
+ self.next_ip_address()
+
+ self.self_ip = self_ip
+ if fixed_ip:
+ self.ip_addr = fixed_ip
+ else:
+ self.ip_addr = self.next_ip_address()
+ if not reuse:
+ def f():
+ if self.br_type == BRIDGE_TYPE_DOCKER:
+ gw = "--gateway %s" % self.ip_addr.split('/')[0]
+ v6 = ''
+ if self.subnet.version == 6:
+ v6 = '--ipv6'
+ cmd = ("docker network create --driver bridge %s "
+ "%s --subnet %s %s" % (v6, gw, subnet, self.name))
+ elif self.br_type == BRIDGE_TYPE_BRCTL:
+ cmd = "ip link add {0} type bridge".format(self.name)
+ elif self.br_type == BRIDGE_TYPE_OVS:
+ cmd = "ovs-vsctl add-br {0}".format(self.name)
+ else:
+ raise ValueError('Unsupported br_type: %s' % self.br_type)
+ self.delete()
+ self.execute(cmd, sudo=True, retry=True)
+ try_several_times(f)
+ if not self.docker_nw:
+ self.execute("ip link set up dev {0}".format(self.name),
+ sudo=True, retry=True)
+
+ if not self.docker_nw and self_ip:
+ ips = self.check_br_addr(self.name)
+ for key, ip in ips.items():
+ if self.subnet.version == key:
+ self.execute(
+ "ip addr del {0} dev {1}".format(ip, self.name),
+ sudo=True, retry=True)
+ self.execute(
+ "ip addr add {0} dev {1}".format(self.ip_addr, self.name),
+ sudo=True, retry=True)
+ self.ctns = []
+
+ def get_bridges_dc(self):
+ out = self.execute('docker network ls', sudo=True, retry=True)
+ bridges = []
+ for line in out.splitlines()[1:]:
+ bridges.append(line.split()[1])
+ return bridges
+
+ def get_bridges_brctl(self):
+ out = self.execute('brctl show', retry=True)
+ bridges = []
+ for line in out.splitlines()[1:]:
+ bridges.append(line.split()[0])
+ return bridges
+
+ def get_bridges_ovs(self):
+ out = self.execute('ovs-vsctl list-br', sudo=True, retry=True)
+ return out.splitlines()
+
+ def get_bridges(self):
+ if self.br_type == BRIDGE_TYPE_DOCKER:
+ return self.get_bridges_dc()
+ elif self.br_type == BRIDGE_TYPE_BRCTL:
+ return self.get_bridges_brctl()
+ elif self.br_type == BRIDGE_TYPE_OVS:
+ return self.get_bridges_ovs()
+
+ def exist(self):
+ return self.name in self.get_bridges()
+
+ def execute(self, cmd, capture=True, sudo=False, retry=False):
+ if sudo:
+ m = self.cmd.sudo
+ else:
+ m = self.cmd.execute
+ if retry:
+ return m(cmd, capture=capture, try_times=3, interval=1)
+ else:
+ return m(cmd, capture=capture)
+
+ def check_br_addr(self, br):
+ ips = {}
+ cmd = "ip a show dev %s" % br
+ for line in self.execute(cmd, sudo=True).split('\n'):
+ if line.strip().startswith("inet "):
+ elems = [e.strip() for e in line.strip().split(' ')]
+ ips[4] = elems[1]
+ elif line.strip().startswith("inet6 "):
+ elems = [e.strip() for e in line.strip().split(' ')]
+ ips[6] = elems[1]
+ return ips
+
+ def next_ip_address(self):
+ return "{0}/{1}".format(next(self._ip_generator),
+ self.subnet.prefixlen)
+
+ def addif(self, ctn):
+ name = ctn.next_if_name()
+ self.ctns.append(ctn)
+ ip_address = None
+ if self.docker_nw:
+ ipv4 = None
+ ipv6 = None
+ ip_address = self.next_ip_address()
+ ip_address_ip = ip_address.split('/')[0]
+ version = 4
+ if netaddr.IPNetwork(ip_address).version == 6:
+ version = 6
+ opt_ip = "--ip %s" % ip_address_ip
+ if version == 4:
+ ipv4 = ip_address
+ else:
+ opt_ip = "--ip6 %s" % ip_address_ip
+ ipv6 = ip_address
+ cmd = "docker network connect %s %s %s" % (
+ opt_ip, self.name, ctn.docker_name())
+ self.execute(cmd, sudo=True)
+ ctn.set_addr_info(bridge=self.name, ipv4=ipv4, ipv6=ipv6,
+ ifname=name)
+ else:
+ if self.with_ip:
+ ip_address = self.next_ip_address()
+ version = 4
+ if netaddr.IPNetwork(ip_address).version == 6:
+ version = 6
+ ctn.pipework(self, ip_address, name, version=version)
+ else:
+ ctn.pipework(self, '0/0', name)
+ return ip_address
+
+ def delete(self, check_exist=True):
+ if check_exist:
+ if not self.exist():
+ return
+ if self.br_type == BRIDGE_TYPE_DOCKER:
+ self.execute("docker network rm %s" % self.name,
+ sudo=True, retry=True)
+ elif self.br_type == BRIDGE_TYPE_BRCTL:
+ self.execute("ip link set down dev %s" % self.name,
+ sudo=True, retry=True)
+ self.execute(
+ "ip link delete %s type bridge" % self.name,
+ sudo=True, retry=True)
+ elif self.br_type == BRIDGE_TYPE_OVS:
+ self.execute(
+ "ovs-vsctl del-br %s" % self.name,
+ sudo=True, retry=True)
+
+
+class Container(object):
+ def __init__(self, name, image=None):
+ self.name = name
+ self.image = image
+ self.shared_volumes = []
+ self.ip_addrs = []
+ self.ip6_addrs = []
+ self.is_running = False
+ self.eths = []
+ self.id = None
+
+ self.cmd = Command()
+ self.remove()
+
+ def docker_name(self):
+ if TEST_PREFIX == DEFAULT_TEST_PREFIX:
+ return self.name
+ return '{0}_{1}'.format(TEST_PREFIX, self.name)
+
+ def get_docker_id(self):
+ if self.id:
+ return self.id
+ else:
+ return self.docker_name()
+
+ def next_if_name(self):
+ name = 'eth{0}'.format(len(self.eths) + 1)
+ self.eths.append(name)
+ return name
+
+ def set_addr_info(self, bridge, ipv4=None, ipv6=None, ifname='eth0'):
+ if ipv4:
+ self.ip_addrs.append((ifname, ipv4, bridge))
+ if ipv6:
+ self.ip6_addrs.append((ifname, ipv6, bridge))
+
+ def get_addr_info(self, bridge, ipv=4):
+ addrinfo = {}
+ if ipv == 4:
+ ip_addrs = self.ip_addrs
+ elif ipv == 6:
+ ip_addrs = self.ip6_addrs
+ else:
+ return None
+ for addr in ip_addrs:
+ if addr[2] == bridge:
+ addrinfo[addr[1]] = addr[0]
+ return addrinfo
+
+ def execute(self, cmd, capture=True, sudo=False, retry=False):
+ if sudo:
+ m = self.cmd.sudo
+ else:
+ m = self.cmd.execute
+ if retry:
+ return m(cmd, capture=capture, try_times=3, interval=1)
+ else:
+ return m(cmd, capture=capture)
+
+ def dcexec(self, cmd, capture=True, retry=False):
+ if retry:
+ return self.cmd.sudo(cmd, capture=capture, try_times=3, interval=1)
+ else:
+ return self.cmd.sudo(cmd, capture=capture)
+
+ def exec_on_ctn(self, cmd, capture=True, detach=False):
+ name = self.docker_name()
+ flag = '-d' if detach else ''
+ return self.dcexec('docker exec {0} {1} {2}'.format(
+ flag, name, cmd), capture=capture)
+
+ def get_containers(self, allctn=False):
+ cmd = 'docker ps --no-trunc=true'
+ if allctn:
+ cmd += ' --all=true'
+ out = self.dcexec(cmd, retry=True)
+ containers = []
+ for line in out.splitlines()[1:]:
+ containers.append(line.split()[-1])
+ return containers
+
+ def exist(self, allctn=False):
+ return self.docker_name() in self.get_containers(allctn=allctn)
+
+ 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} --hostname {0} -id {1}".format(self.docker_name(),
+ self.image)
+ self.id = self.dcexec(str(c), retry=True)
+ self.is_running = True
+ self.exec_on_ctn("ip li set up dev lo")
+ ipv4 = None
+ ipv6 = None
+ for line in self.exec_on_ctn("ip a show dev eth0").split('\n'):
+ if line.strip().startswith("inet "):
+ elems = [e.strip() for e in line.strip().split(' ')]
+ ipv4 = elems[1]
+ elif line.strip().startswith("inet6 "):
+ elems = [e.strip() for e in line.strip().split(' ')]
+ ipv6 = elems[1]
+ self.set_addr_info(bridge='docker0', ipv4=ipv4, ipv6=ipv6,
+ ifname='eth0')
+ return 0
+
+ def stop(self, check_exist=True):
+ if check_exist:
+ if not self.exist(allctn=False):
+ return
+ ctn_id = self.get_docker_id()
+ out = self.dcexec('docker stop -t 0 %s' % ctn_id, retry=True)
+ self.is_running = False
+ return out
+
+ def remove(self, check_exist=True):
+ if check_exist:
+ if not self.exist(allctn=True):
+ return
+ ctn_id = self.get_docker_id()
+ out = self.dcexec('docker rm -f %s' % ctn_id, retry=True)
+ self.is_running = False
+ return out
+
+ def pipework(self, bridge, ip_addr, intf_name="", version=4):
+ if not self.is_running:
+ LOG.warning('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"
+ ipv4 = None
+ ipv6 = None
+ if version == 4:
+ ipv4 = ip_addr
+ else:
+ c << '-a 6'
+ ipv6 = ip_addr
+ c << "{0} {1}".format(self.docker_name(), ip_addr)
+ self.set_addr_info(bridge=bridge.name, ipv4=ipv4, ipv6=ipv6,
+ ifname=intf_name)
+ self.execute(str(c), sudo=True, retry=True)
+
+ def get_pid(self):
+ if self.is_running:
+ cmd = "docker inspect -f '{{.State.Pid}}' %s" % self.docker_name()
+ return int(self.dcexec(cmd))
+ return -1
+
+ def start_tcpdump(self, interface=None, filename=None):
+ if not interface:
+ interface = "eth0"
+ if not filename:
+ filename = "{0}/{1}.dump".format(
+ self.shared_volumes[0][1], interface)
+ self.exec_on_ctn(
+ "tcpdump -i {0} -w {1}".format(interface, filename),
+ detach=True)
+
+
+class BGPContainer(Container):
+
+ WAIT_FOR_BOOT = 1
+ RETRY_INTERVAL = 5
+ DEFAULT_PEER_ARG = {'neigh_addr': '',
+ 'passwd': None,
+ 'vpn': False,
+ 'flowspec': False,
+ 'is_rs_client': False,
+ 'is_rr_client': False,
+ 'cluster_id': None,
+ 'policies': None,
+ 'passive': False,
+ 'local_addr': '',
+ 'as2': False,
+ 'graceful_restart': None,
+ 'local_as': None,
+ 'prefix_limit': None}
+ default_peer_keys = sorted(DEFAULT_PEER_ARG.keys())
+ DEFAULT_ROUTE_ARG = {'prefix': None,
+ 'rf': 'ipv4',
+ 'attr': None,
+ 'next-hop': None,
+ 'as-path': None,
+ 'community': None,
+ 'med': None,
+ 'local-pref': None,
+ 'extended-community': None,
+ 'matchs': None,
+ 'thens': None}
+ default_route_keys = sorted(DEFAULT_ROUTE_ARG.keys())
+
+ def __init__(self, name, asn, router_id, ctn_image_name=None):
+ self.config_dir = TEST_BASE_DIR
+ if TEST_PREFIX:
+ self.config_dir = os.path.join(self.config_dir, TEST_PREFIX)
+ self.config_dir = os.path.join(self.config_dir, name)
+ self.asn = asn
+ self.router_id = router_id
+ self.peers = {}
+ self.routes = {}
+ self.policies = {}
+ super(BGPContainer, self).__init__(name, ctn_image_name)
+ self.execute(
+ 'rm -rf {0}'.format(self.config_dir), sudo=True)
+ self.execute('mkdir -p {0}'.format(self.config_dir))
+ self.execute('chmod 777 {0}'.format(self.config_dir))
+
+ def __repr__(self):
+ return str({'name': self.name, 'asn': self.asn,
+ 'router_id': self.router_id})
+
+ def run(self, wait=False, w_time=WAIT_FOR_BOOT):
+ self.create_config()
+ super(BGPContainer, self).run()
+ if wait:
+ time.sleep(w_time)
+ return w_time
+
+ def add_peer(self, peer, bridge='', reload_config=True, v6=False,
+ peer_info=None):
+ peer_info = peer_info or {}
+ self.peers[peer] = self.DEFAULT_PEER_ARG.copy()
+ self.peers[peer].update(peer_info)
+ peer_keys = sorted(self.peers[peer].keys())
+ if peer_keys != self.default_peer_keys:
+ raise Exception("argument error peer_info: %s" % peer_info)
+
+ neigh_addr = ''
+ local_addr = ''
+ it = itertools.product(self.ip_addrs, peer.ip_addrs)
+ if v6:
+ it = itertools.product(self.ip6_addrs, peer.ip6_addrs)
+
+ for me, you in it:
+ if bridge != '' and bridge != me[2]:
+ continue
+ if me[2] == you[2]:
+ neigh_addr = you[1]
+ local_addr = me[1]
+ if v6:
+ addr, mask = local_addr.split('/')
+ local_addr = "{0}%{1}/{2}".format(addr, me[0], mask)
+ break
+
+ if neigh_addr == '':
+ raise Exception('peer {0} seems not ip reachable'.format(peer))
+
+ if not self.peers[peer]['policies']:
+ self.peers[peer]['policies'] = {}
+
+ self.peers[peer]['neigh_addr'] = neigh_addr
+ self.peers[peer]['local_addr'] = local_addr
+ if self.is_running and reload_config:
+ self.create_config()
+ self.reload_config()
+
+ def del_peer(self, peer, reload_config=True):
+ del self.peers[peer]
+ if self.is_running and reload_config:
+ self.create_config()
+ self.reload_config()
+
+ def disable_peer(self, peer):
+ raise NotImplementedError()
+
+ def enable_peer(self, peer):
+ raise NotImplementedError()
+
+ def log(self):
+ return self.execute('cat {0}/*.log'.format(self.config_dir))
+
+ def add_route(self, route, reload_config=True, route_info=None):
+ route_info = route_info or {}
+ self.routes[route] = self.DEFAULT_ROUTE_ARG.copy()
+ self.routes[route].update(route_info)
+ route_keys = sorted(self.routes[route].keys())
+ if route_keys != self.default_route_keys:
+ raise Exception("argument error route_info: %s" % route_info)
+ self.routes[route]['prefix'] = route
+ if self.is_running and reload_config:
+ self.create_config()
+ self.reload_config()
+
+ def add_policy(self, policy, peer, typ, default='accept',
+ reload_config=True):
+ self.set_default_policy(peer, typ, default)
+ self.define_policy(policy)
+ self.assign_policy(peer, policy, typ)
+ if self.is_running and reload_config:
+ self.create_config()
+ self.reload_config()
+
+ def set_default_policy(self, peer, typ, default):
+ if (typ in ['in', 'out', 'import', 'export'] and
+ default in ['reject', 'accept']):
+ if 'default-policy' not in self.peers[peer]:
+ self.peers[peer]['default-policy'] = {}
+ self.peers[peer]['default-policy'][typ] = default
+ else:
+ raise Exception('wrong type or default')
+
+ def define_policy(self, policy):
+ self.policies[policy['name']] = policy
+
+ def assign_policy(self, peer, policy, typ):
+ if peer not in self.peers:
+ raise Exception('peer {0} not found'.format(peer.name))
+ name = policy['name']
+ if name not in self.policies:
+ raise Exception('policy {0} not found'.format(name))
+ self.peers[peer]['policies'][typ] = policy
+
+ def get_local_rib(self, peer, rf):
+ raise NotImplementedError()
+
+ def get_global_rib(self, rf):
+ raise NotImplementedError()
+
+ def get_neighbor_state(self, peer_id):
+ raise NotImplementedError()
+
+ def get_reachablily(self, prefix, timeout=20):
+ version = netaddr.IPNetwork(prefix).version
+ addr = prefix.split('/')[0]
+ if version == 4:
+ ping_cmd = 'ping'
+ elif version == 6:
+ ping_cmd = 'ping6'
+ else:
+ raise Exception(
+ 'unsupported route family: {0}'.format(version))
+ cmd = '/bin/bash -c "/bin/{0} -c 1 -w 1 {1} | xargs echo"'.format(
+ ping_cmd, addr)
+ interval = 1
+ count = 0
+ while True:
+ res = self.exec_on_ctn(cmd)
+ LOG.info(res)
+ if '1 packets received' in res and '0% packet loss':
+ break
+ time.sleep(interval)
+ count += interval
+ if count >= timeout:
+ raise Exception('timeout')
+ return True
+
+ def wait_for(self, expected_state, peer, timeout=120):
+ interval = 1
+ count = 0
+ while True:
+ state = self.get_neighbor_state(peer)
+ LOG.info("%s's peer %s state: %s",
+ self.router_id, peer.router_id, state)
+ if state == expected_state:
+ return
+
+ time.sleep(interval)
+ count += interval
+ if count >= timeout:
+ raise Exception('timeout')
+
+ def add_static_route(self, network, next_hop):
+ cmd = '/sbin/ip route add {0} via {1}'.format(network, next_hop)
+ self.exec_on_ctn(cmd)
+
+ def set_ipv6_forward(self):
+ cmd = 'sysctl -w net.ipv6.conf.all.forwarding=1'
+ self.exec_on_ctn(cmd)
+
+ def create_config(self):
+ raise NotImplementedError()
+
+ def reload_config(self):
+ raise NotImplementedError()
diff --git a/ryu/lib/docker/install_docker_test_pkg.sh b/ryu/lib/docker/install_docker_test_pkg.sh
new file mode 100644
index 0000000..a771dfc
--- /dev/null
+++ b/ryu/lib/docker/install_docker_test_pkg.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+set -ex
+
+RYU_PATH=`dirname $0`
+
+source ${RYU_PATH}/install_docker_test_pkg_common.sh
+
+function add_docker_aptline {
+ sudo apt-get update
+ if ! apt-cache search docker-engine | grep docker-engine; then
+ VER=`lsb_release -r`
+ if echo $VER | grep 12.04; then
+ REL_NAME=precise
+ elif echo $VER | grep 14.04; then
+ REL_NAME=trusty
+ elif echo $VER | grep 15.10; then
+ REL_NAME=wily
+ elif echo $VER | grep 16.04; then
+ REL_NAME=xenial
+ else
+ retrun 1
+ fi
+ RELEASE=ubuntu-$REL_NAME
+ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
+ sudo sh -c "echo deb https://apt.dockerproject.org/repo $RELEASE main > /etc/apt/sources.list.d/docker.list"
+ fi
+}
+
+init_variables
+process_options "$@"
+
+if [ $APTLINE_DOCKER -eq 1 ]; then
+ add_docker_aptline
+fi
+
+sudo apt-get update
+if apt-cache search docker-engine | grep docker-engine; then
+ DOCKER_PKG=docker-engine
+else
+ DOCKER_PKG=docker.io
+fi
+sudo apt-get install -y $DOCKER_PKG
+install_depends_pkg
diff --git a/ryu/lib/docker/install_docker_test_pkg_common.sh b/ryu/lib/docker/install_docker_test_pkg_common.sh
new file mode 100644
index 0000000..44a3e10
--- /dev/null
+++ b/ryu/lib/docker/install_docker_test_pkg_common.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+set -ex
+
+function init_variables {
+ APTLINE_DOCKER=0
+ DIR_BASE=/tmp
+}
+
+function process_options {
+ local max
+ local i
+ max=$#
+ i=1
+ while [ $i -le $max ]; do
+ case "$1" in
+ -a|--add-docker-aptline)
+ APTLINE_DOCKER=1
+ ;;
+ -d|--download-dir)
+ shift; ((i++))
+ DIR_BASE=$1
+ ;;
+ esac
+ shift; ((i++))
+ done
+}
+
+function install_pipework {
+ if ! which /usr/local/bin/pipework >/dev/null
+ then
+ sudo rm -rf $DIR_BASE/pipework
+ git clone https://github.com/jpetazzo/pipework.git $DIR_BASE/pipework
+ sudo install -m 0755 $DIR_BASE/pipework/pipework /usr/local/bin/pipework
+ fi
+}
+
+function install_depends_pkg {
+ install_pipework
+}
diff --git a/ryu/lib/docker/install_docker_test_pkg_for_travis.sh b/ryu/lib/docker/install_docker_test_pkg_for_travis.sh
new file mode 100644
index 0000000..d8c3b49
--- /dev/null
+++ b/ryu/lib/docker/install_docker_test_pkg_for_travis.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+set -ex
+
+RYU_PATH=`dirname $0`
+
+source ${RYU_PATH}/install_docker_test_pkg_common.sh
+
+init_variables
+process_options "$@"
+
+sudo apt-get update
+install_depends_pkg
diff --git a/ryu/lib/docker/quagga.py b/ryu/lib/docker/quagga.py
new file mode 100644
index 0000000..9b6d218
--- /dev/null
+++ b/ryu/lib/docker/quagga.py
@@ -0,0 +1,332 @@
+# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
+#
+# This is based on the following
+# https://github.com/osrg/gobgp/test/lib/quagga.py
+#
+# 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 __future__ import absolute_import
+
+import logging
+import os
+
+import netaddr
+
+from . import docker_base as base
+
+LOG = logging.getLogger(__name__)
+
+
+class QuaggaBGPContainer(base.BGPContainer):
+
+ WAIT_FOR_BOOT = 1
+ SHARED_VOLUME = '/etc/quagga'
+
+ def __init__(self, name, asn, router_id, ctn_image_name, zebra=False):
+ super(QuaggaBGPContainer, self).__init__(name, asn, router_id,
+ ctn_image_name)
+ self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
+ self.zebra = zebra
+ self._create_config_debian()
+
+ def run(self, wait=False, w_time=WAIT_FOR_BOOT):
+ w_time = super(QuaggaBGPContainer,
+ self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
+ return w_time
+
+ def get_global_rib(self, prefix='', rf='ipv4'):
+ rib = []
+ if prefix != '':
+ return self.get_global_rib_with_prefix(prefix, rf)
+
+ out = self.vtysh('show bgp {0} unicast'.format(rf), config=False)
+ if out.startswith('No BGP network exists'):
+ return rib
+
+ read_next = False
+
+ for line in out.split('\n'):
+ ibgp = False
+ if line[:2] == '*>':
+ line = line[2:]
+ if line[0] == 'i':
+ line = line[1:]
+ ibgp = True
+ elif not read_next:
+ continue
+
+ elems = line.split()
+
+ if len(elems) == 1:
+ read_next = True
+ prefix = elems[0]
+ continue
+ elif read_next:
+ nexthop = elems[0]
+ else:
+ prefix = elems[0]
+ nexthop = elems[1]
+ read_next = False
+
+ rib.append({'prefix': prefix, 'nexthop': nexthop,
+ 'ibgp': ibgp})
+
+ return rib
+
+ def get_global_rib_with_prefix(self, prefix, rf):
+ rib = []
+
+ lines = [line.strip() for line in self.vtysh(
+ 'show bgp {0} unicast {1}'.format(rf, prefix),
+ config=False).split('\n')]
+
+ if lines[0] == '% Network not in table':
+ return rib
+
+ lines = lines[2:]
+
+ if lines[0].startswith('Not advertised'):
+ lines.pop(0) # another useless line
+ elif lines[0].startswith('Advertised to non peer-group peers:'):
+ lines = lines[2:] # other useless lines
+ else:
+ raise Exception('unknown output format {0}'.format(lines))
+
+ if lines[0] == 'Local':
+ aspath = []
+ else:
+ aspath = [int(asn) for asn in lines[0].split()]
+
+ nexthop = lines[1].split()[0].strip()
+ info = [s.strip(',') for s in lines[2].split()]
+ attrs = []
+ if 'metric' in info:
+ med = info[info.index('metric') + 1]
+ attrs.append({'type': base.BGP_ATTR_TYPE_MULTI_EXIT_DISC,
+ 'metric': int(med)})
+ if 'localpref' in info:
+ localpref = info[info.index('localpref') + 1]
+ attrs.append({'type': base.BGP_ATTR_TYPE_LOCAL_PREF,
+ 'value': int(localpref)})
+
+ rib.append({'prefix': prefix, 'nexthop': nexthop,
+ 'aspath': aspath, 'attrs': attrs})
+
+ return rib
+
+ def get_neighbor_state(self, peer):
+ if peer not in self.peers:
+ raise Exception('not found peer {0}'.format(peer.router_id))
+
+ neigh_addr = self.peers[peer]['neigh_addr'].split('/')[0]
+
+ info = [l.strip() for l in self.vtysh(
+ 'show bgp neighbors {0}'.format(neigh_addr),
+ config=False).split('\n')]
+
+ if not info[0].startswith('BGP neighbor is'):
+ raise Exception('unknown format')
+
+ idx1 = info[0].index('BGP neighbor is ')
+ idx2 = info[0].index(',')
+ n_addr = info[0][idx1 + len('BGP neighbor is '):idx2]
+ if n_addr == neigh_addr:
+ idx1 = info[2].index('= ')
+ state = info[2][idx1 + len('= '):]
+ if state.startswith('Idle'):
+ return base.BGP_FSM_IDLE
+ elif state.startswith('Active'):
+ return base.BGP_FSM_ACTIVE
+ elif state.startswith('Established'):
+ return base.BGP_FSM_ESTABLISHED
+ else:
+ return state
+
+ raise Exception('not found peer {0}'.format(peer.router_id))
+
+ def send_route_refresh(self):
+ self.vtysh('clear ip bgp * soft', config=False)
+
+ def create_config(self):
+ zebra = 'no'
+ self._create_config_bgp()
+ if self.zebra:
+ zebra = 'yes'
+ self._create_config_zebra()
+ self._create_config_daemons(zebra)
+
+ def _create_config_debian(self):
+ c = base.CmdBuffer()
+ c << 'vtysh_enable=yes'
+ c << 'zebra_options=" --daemon -A 127.0.0.1"'
+ c << 'bgpd_options=" --daemon -A 127.0.0.1"'
+ c << 'ospfd_options=" --daemon -A 127.0.0.1"'
+ c << 'ospf6d_options=" --daemon -A ::1"'
+ c << 'ripd_options=" --daemon -A 127.0.0.1"'
+ c << 'ripngd_options=" --daemon -A ::1"'
+ c << 'isisd_options=" --daemon -A 127.0.0.1"'
+ c << 'babeld_options=" --daemon -A 127.0.0.1"'
+ c << 'watchquagga_enable=yes'
+ c << 'watchquagga_options=(--daemon)'
+ with open('{0}/debian.conf'.format(self.config_dir), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def _create_config_daemons(self, zebra='no'):
+ c = base.CmdBuffer()
+ c << 'zebra=%s' % zebra
+ c << 'bgpd=yes'
+ c << 'ospfd=no'
+ c << 'ospf6d=no'
+ c << 'ripd=no'
+ c << 'ripngd=no'
+ c << 'isisd=no'
+ c << 'babeld=no'
+ with open('{0}/daemons'.format(self.config_dir), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def _create_config_bgp(self):
+
+ c = base.CmdBuffer()
+ c << 'hostname bgpd'
+ c << 'password zebra'
+ c << 'router bgp {0}'.format(self.asn)
+ c << 'bgp router-id {0}'.format(self.router_id)
+ if any(info['graceful_restart'] for info in self.peers.values()):
+ c << 'bgp graceful-restart'
+
+ version = 4
+ for peer, info in self.peers.items():
+ version = netaddr.IPNetwork(info['neigh_addr']).version
+ n_addr = info['neigh_addr'].split('/')[0]
+ if version == 6:
+ c << 'no bgp default ipv4-unicast'
+
+ c << 'neighbor {0} remote-as {1}'.format(n_addr, peer.asn)
+ if info['is_rs_client']:
+ c << 'neighbor {0} route-server-client'.format(n_addr)
+ for typ, p in info['policies'].items():
+ c << 'neighbor {0} route-map {1} {2}'.format(n_addr, p['name'],
+ typ)
+ if info['passwd']:
+ c << 'neighbor {0} password {1}'.format(n_addr, info['passwd'])
+ if info['passive']:
+ c << 'neighbor {0} passive'.format(n_addr)
+ if version == 6:
+ c << 'address-family ipv6 unicast'
+ c << 'neighbor {0} activate'.format(n_addr)
+ c << 'exit-address-family'
+
+ for route in self.routes.values():
+ 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'
+ c << 'redistribute connected'
+ c << 'exit-address-family'
+ else:
+ c << 'redistribute connected'
+
+ for name, policy in self.policies.items():
+ c << 'access-list {0} {1} {2}'.format(name, policy['type'],
+ policy['match'])
+ c << 'route-map {0} permit 10'.format(name)
+ c << 'match ip address {0}'.format(name)
+ c << 'set metric {0}'.format(policy['med'])
+
+ c << 'debug bgp as4'
+ c << 'debug bgp fsm'
+ c << 'debug bgp updates'
+ c << 'debug bgp events'
+ c << 'log file {0}/bgpd.log'.format(self.SHARED_VOLUME)
+
+ with open('{0}/bgpd.conf'.format(self.config_dir), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def _create_config_zebra(self):
+ c = base.CmdBuffer()
+ c << 'hostname zebra'
+ c << 'password zebra'
+ c << 'log file {0}/zebra.log'.format(self.SHARED_VOLUME)
+ c << 'debug zebra packet'
+ c << 'debug zebra kernel'
+ c << 'debug zebra rib'
+ c << ''
+
+ with open('{0}/zebra.conf'.format(self.config_dir), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def vtysh(self, cmd, config=True):
+ if not isinstance(cmd, list):
+ cmd = [cmd]
+ cmd = ' '.join("-c '{0}'".format(c) for c in cmd)
+ if config:
+ return self.exec_on_ctn(
+ "vtysh -d bgpd -c 'en' -c 'conf t' -c "
+ "'router bgp {0}' {1}".format(self.asn, cmd),
+ capture=True)
+ else:
+ return self.exec_on_ctn("vtysh -d bgpd {0}".format(cmd),
+ capture=True)
+
+ def reload_config(self):
+ daemon = []
+ daemon.append('bgpd')
+ if self.zebra:
+ daemon.append('zebra')
+ for d in daemon:
+ cmd = '/usr/bin/pkill {0} -SIGHUP'.format(d)
+ self.exec_on_ctn(cmd, capture=True)
+
+
+class RawQuaggaBGPContainer(QuaggaBGPContainer):
+ def __init__(self, name, config, ctn_image_name,
+ zebra=False):
+ asn = None
+ router_id = None
+ for line in config.split('\n'):
+ line = line.strip()
+ if line.startswith('router bgp'):
+ asn = int(line[len('router bgp'):].strip())
+ if line.startswith('bgp router-id'):
+ router_id = line[len('bgp router-id'):].strip()
+ if not asn:
+ raise Exception('asn not in quagga config')
+ if not router_id:
+ raise Exception('router-id not in quagga config')
+ self.config = config
+ super(RawQuaggaBGPContainer, self).__init__(name, asn, router_id,
+ ctn_image_name, zebra)
+
+ def create_config(self):
+ with open(os.path.join(self.config_dir, 'bgpd.conf'), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(self.config)
+ f.writelines(self.config)
diff --git a/ryu/lib/docker/ryubgp.py b/ryu/lib/docker/ryubgp.py
new file mode 100644
index 0000000..8fe16f4
--- /dev/null
+++ b/ryu/lib/docker/ryubgp.py
@@ -0,0 +1,212 @@
+# Copyright (C) 2016 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 __future__ import absolute_import
+
+import logging
+import os
+import time
+
+from . import docker_base as base
+
+LOG = logging.getLogger(__name__)
+
+
+class RyuBGPContainer(base.BGPContainer):
+
+ WAIT_FOR_BOOT = 1
+ SHARED_VOLUME = '/etc/ryu'
+
+ def __init__(self, name, asn, router_id, ctn_image_name):
+ super(RyuBGPContainer, self).__init__(name, asn, router_id,
+ ctn_image_name)
+ self.RYU_CONF = os.path.join(self.config_dir, 'ryu.conf')
+ self.SHARED_RYU_CONF = os.path.join(self.SHARED_VOLUME, 'ryu.conf')
+ self.SHARED_BGP_CONF = os.path.join(self.SHARED_VOLUME, 'bgp_conf.py')
+ self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
+
+ def _create_config_ryu(self):
+ c = base.CmdBuffer()
+ c << '[DEFAULT]'
+ c << 'verbose=True'
+ c << 'log_file=/etc/ryu/manager.log'
+ with open(self.RYU_CONF, 'w') as f:
+ LOG.info("[%s's new config]" % self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def _create_config_ryu_bgp(self):
+ c = base.CmdBuffer()
+ c << 'import os'
+ c << ''
+ c << 'BGP = {'
+ c << " 'local_as': %s," % str(self.asn)
+ c << " 'router_id': '%s'," % self.router_id
+ c << " 'neighbors': ["
+ c << " {"
+ for peer, info in self.peers.items():
+ n_addr = info['neigh_addr'].split('/')[0]
+ c << " 'address': '%s'," % n_addr
+ c << " 'remote_as': %s," % str(peer.asn)
+ c << " 'enable_ipv4': True,"
+ c << " 'enable_ipv6': True,"
+ c << " 'enable_vpnv4': True,"
+ c << " 'enable_vpnv6': True,"
+ c << ' },'
+ c << ' ],'
+ c << " 'routes': ["
+ for route in self.routes.values():
+ c << " {"
+ c << " 'prefix': '%s'," % route['prefix']
+ c << " },"
+ c << " ],"
+ c << "}"
+ log_conf = """LOGGING = {
+
+ # We use python logging package for logging.
+ 'version': 1,
+ 'disable_existing_loggers': False,
+
+ 'formatters': {
+ 'verbose': {
+ 'format': '%(levelname)s %(asctime)s %(module)s ' +
+ '[%(process)d %(thread)d] %(message)s'
+ },
+ 'simple': {
+ 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)s ' +
+ '%(message)s'
+ },
+ 'stats': {
+ 'format': '%(message)s'
+ },
+ },
+
+ 'handlers': {
+ # Outputs log to console.
+ 'console': {
+ 'level': 'DEBUG',
+ 'class': 'logging.StreamHandler',
+ 'formatter': 'simple'
+ },
+ 'console_stats': {
+ 'level': 'DEBUG',
+ 'class': 'logging.StreamHandler',
+ 'formatter': 'stats'
+ },
+ # Rotates log file when its size reaches 10MB.
+ 'log_file': {
+ 'level': 'DEBUG',
+ 'class': 'logging.handlers.RotatingFileHandler',
+ 'filename': os.path.join('.', 'bgpspeaker.log'),
+ 'maxBytes': '10000000',
+ 'formatter': 'verbose'
+ },
+ 'stats_file': {
+ 'level': 'DEBUG',
+ 'class': 'logging.handlers.RotatingFileHandler',
+ 'filename': os.path.join('.', 'statistics_bgps.log'),
+ 'maxBytes': '10000000',
+ 'formatter': 'stats'
+ },
+ },
+
+ # Fine-grained control of logging per instance.
+ 'loggers': {
+ 'bgpspeaker': {
+ 'handlers': ['console', 'log_file'],
+ 'handlers': ['console'],
+ 'level': 'DEBUG',
+ 'propagate': False,
+ },
+ 'stats': {
+ 'handlers': ['stats_file', 'console_stats'],
+ 'level': 'INFO',
+ 'propagate': False,
+ 'formatter': 'stats',
+ },
+ },
+
+ # Root loggers.
+ 'root': {
+ 'handlers': ['console', 'log_file'],
+ 'level': 'DEBUG',
+ 'propagate': True,
+ },
+}"""
+ c << log_conf
+ with open(os.path.join(self.config_dir, 'bgp_conf.py'), 'w') as f:
+ LOG.info("[%s's new config]", self.name)
+ LOG.info(str(c))
+ f.writelines(str(c))
+
+ def create_config(self):
+ self._create_config_ryu()
+ self._create_config_ryu_bgp()
+
+ def is_running_ryu(self):
+ results = self.exec_on_ctn('ps ax')
+ running = False
+ for line in results.split('\n')[1:]:
+ if 'ryu-manager' in line:
+ running = True
+ return running
+
+ def start_ryubgp(self, check_running=True, retry=False):
+ if check_running:
+ if self.is_running_ryu():
+ return True
+ result = False
+ if retry:
+ try_times = 3
+ else:
+ try_times = 1
+ cmd = "ryu-manager --verbose "
+ cmd += "--config-file %s " % self.SHARED_RYU_CONF
+ cmd += "--bgp-app-config-file %s " % self.SHARED_BGP_CONF
+ cmd += "ryu.services.protocols.bgp.application"
+ for _ in range(try_times):
+ self.exec_on_ctn(cmd, detach=True)
+ if self.is_running_ryu():
+ result = True
+ break
+ time.sleep(1)
+ return result
+
+ def stop_ryubgp(self, check_running=True, retry=False):
+ if check_running:
+ if not self.is_running_ryu():
+ return True
+ result = False
+ if retry:
+ try_times = 3
+ else:
+ try_times = 1
+ for _ in range(try_times):
+ cmd = '/usr/bin/pkill ryu-manager -SIGTERM'
+ self.exec_on_ctn(cmd)
+ if not self.is_running_ryu():
+ result = True
+ break
+ time.sleep(1)
+ return result
+
+ def run(self, wait=False, w_time=WAIT_FOR_BOOT):
+ w_time = super(RyuBGPContainer,
+ self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
+ return w_time
+
+ def reload_config(self):
+ self.stop_ryubgp(retry=True)
+ self.start_ryubgp(retry=True)
diff --git a/ryu/tests/integrated/common/__init__.py b/ryu/tests/integrated/common/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/ryu/tests/integrated/common/docker_base.py b/ryu/tests/integrated/common/docker_base.py
deleted file mode 100644
index 1ae2cc2..0000000
--- a/ryu/tests/integrated/common/docker_base.py
+++ /dev/null
@@ -1,801 +0,0 @@
-# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
-#
-# This is based on the following
-# https://github.com/osrg/gobgp/test/lib/base.py
-#
-# 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 __future__ import absolute_import
-
-import itertools
-import logging
-import os
-import subprocess
-import time
-
-import netaddr
-import six
-
-LOG = logging.getLogger(__name__)
-
-DEFAULT_TEST_PREFIX = ''
-DEFAULT_TEST_BASE_DIR = '/tmp/ctn_docker/bgp'
-TEST_PREFIX = DEFAULT_TEST_PREFIX
-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'
-
-BGP_ATTR_TYPE_ORIGIN = 1
-BGP_ATTR_TYPE_AS_PATH = 2
-BGP_ATTR_TYPE_NEXT_HOP = 3
-BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4
-BGP_ATTR_TYPE_LOCAL_PREF = 5
-BGP_ATTR_TYPE_COMMUNITIES = 8
-BGP_ATTR_TYPE_ORIGINATOR_ID = 9
-BGP_ATTR_TYPE_CLUSTER_LIST = 10
-BGP_ATTR_TYPE_MP_REACH_NLRI = 14
-BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16
-
-BRIDGE_TYPE_DOCKER = 'docker'
-BRIDGE_TYPE_BRCTL = 'brctl'
-BRIDGE_TYPE_OVS = 'ovs'
-
-
-class CommandError(Exception):
- def __init__(self, out):
- super(CommandError, self).__init__()
- self.out = out
-
-
-def try_several_times(f, t=3, s=1):
- e = RuntimeError()
- for _ in range(t):
- try:
- r = f()
- except RuntimeError as e:
- time.sleep(s)
- else:
- return r
- raise e
-
-
-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)
-
-
-class CommandOut(str):
-
- def __new__(cls, stdout, stderr, command, returncode, **kwargs):
- stdout = stdout or ''
- obj = super(CommandOut, cls).__new__(cls, stdout, **kwargs)
- obj.stderr = stderr or ''
- obj.command = command
- obj.returncode = returncode
- return obj
-
-
-class Command(object):
-
- def _execute(self, cmd, capture=False, executable=None):
- """Execute a command using subprocess.Popen()
- :Parameters:
- - out: stdout from subprocess.Popen()
- out has some attributes.
- out.returncode: returncode of subprocess.Popen()
- out.stderr: stderr from subprocess.Popen()
- """
- if capture:
- p_stdout = subprocess.PIPE
- p_stderr = subprocess.PIPE
- else:
- p_stdout = None
- p_stderr = None
- pop = subprocess.Popen(cmd, shell=True, executable=executable,
- stdout=p_stdout,
- stderr=p_stderr)
- __stdout, __stderr = pop.communicate()
- _stdout = six.text_type(__stdout, 'utf-8')
- _stderr = six.text_type(__stderr, 'utf-8')
- out = CommandOut(_stdout, _stderr, cmd, pop.returncode)
- return out
-
- def execute(self, cmd, capture=True, try_times=1, interval=1):
- out = None
- for i in range(try_times):
- out = self._execute(cmd, capture=capture)
- LOG.info(out.command)
- if out.returncode == 0:
- return out
- LOG.error("stdout: %s", out)
- LOG.error("stderr: %s", out.stderr)
- if i + 1 >= try_times:
- break
- time.sleep(interval)
- raise CommandError(out)
-
- def sudo(self, cmd, capture=True, try_times=1, interval=1):
- cmd = 'sudo %s' % cmd
- return self.execute(cmd, capture=capture,
- try_times=try_times, interval=interval)
-
-
-class DockerImage(object):
- def __init__(self, baseimage='ubuntu:16.04'):
- self.baseimage = baseimage
- self.cmd = Command()
-
- def get_images(self):
- out = self.cmd.sudo('sudo docker images')
- images = []
- for line in out.splitlines()[1:]:
- images.append(line.split()[0])
- return images
-
- def exist(self, name):
- return name in self.get_images()
-
- def build(self, tagname, dockerfile_dir):
- self.cmd.sudo(
- "docker build -t {0} {1}".format(tagname, dockerfile_dir),
- try_times=3)
-
- def remove(self, tagname, check_exist=False):
- if check_exist and not self.exist(tagname):
- return tagname
- self.cmd.sudo("docker rmi -f %s" % tagname, try_times=3)
-
- def create_quagga(self, tagname='quagga', image=None, check_exist=False):
- if check_exist and self.exist(tagname):
- return tagname
- workdir = os.path.join(TEST_BASE_DIR, tagname)
- pkges = ' '.join([
- 'telnet',
- 'tcpdump',
- 'quagga',
- ])
- if image:
- use_image = image
- else:
- use_image = self.baseimage
- c = CmdBuffer()
- c << 'FROM %s' % use_image
- c << 'RUN apt-get update'
- c << 'RUN apt-get install -qy --no-install-recommends %s' % pkges
- c << 'CMD /usr/lib/quagga/bgpd'
-
- self.cmd.sudo('rm -rf %s' % workdir)
- self.cmd.execute('mkdir -p %s' % workdir)
- self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
- self.build(tagname, workdir)
- return tagname
-
- def create_ryu(self, tagname='ryu', image=None, check_exist=False):
- if check_exist and self.exist(tagname):
- return tagname
- workdir = os.path.join(TEST_BASE_DIR, tagname)
- workdir_ctn = '/root/osrg/ryu'
- pkges = ' '.join([
- 'tcpdump',
- 'iproute2',
- ])
- if image:
- use_image = image
- else:
- use_image = self.baseimage
- c = CmdBuffer()
- c << 'FROM %s' % use_image
- c << 'ADD ryu %s' % workdir_ctn
- install = ' '.join([
- 'RUN apt-get update',
- '&& apt-get install -qy --no-install-recommends %s' % pkges,
- '&& cd %s' % workdir_ctn,
- # Note: Clean previous builds, because "python setup.py install"
- # might fail if the current directory contains the symlink to
- # Docker host file systems.
- '&& rm -rf *.egg-info/ build/ dist/ .tox/ *.log'
- '&& pip install -r tools/pip-requires -r tools/optional-requires',
- '&& python setup.py install',
- ])
- c << install
-
- self.cmd.sudo('rm -rf %s' % workdir)
- self.cmd.execute('mkdir -p %s' % workdir)
- self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
- self.cmd.execute('cp -r ../ryu %s/' % workdir)
- self.build(tagname, workdir)
- return tagname
-
-
-class Bridge(object):
- def __init__(self, name, subnet='', start_ip=None, end_ip=None,
- with_ip=True, self_ip=False,
- fixed_ip=None, reuse=False,
- br_type='docker'):
- """Manage a bridge
- :Parameters:
- - name: bridge name
- - subnet: network cider to be used in this bridge
- - start_ip: start address of an ip to be used in the subnet
- - end_ip: end address of an ip to be used in the subnet
- - with_ip: specify if assign automatically an ip address
- - self_ip: specify if assign an ip address for the bridge
- - fixed_ip: an ip address to be assigned to the bridge
- - reuse: specify if use an existing bridge
- - br_type: One either in a 'docker', 'brctl' or 'ovs'
- """
- self.cmd = Command()
- self.name = name
- if br_type not in (BRIDGE_TYPE_DOCKER, BRIDGE_TYPE_BRCTL,
- BRIDGE_TYPE_OVS):
- raise Exception("argument error br_type: %s" % br_type)
- self.br_type = br_type
- self.docker_nw = bool(self.br_type == BRIDGE_TYPE_DOCKER)
- if TEST_PREFIX != '':
- self.name = '{0}_{1}'.format(TEST_PREFIX, name)
- self.with_ip = with_ip
- if with_ip:
- self.subnet = netaddr.IPNetwork(subnet)
- if start_ip:
- self.start_ip = start_ip
- else:
- self.start_ip = netaddr.IPAddress(self.subnet.first)
- if end_ip:
- self.end_ip = end_ip
- else:
- self.end_ip = netaddr.IPAddress(self.subnet.last)
-
- def _ip_gen():
- for host in netaddr.IPRange(self.start_ip, self.end_ip):
- yield host
- self._ip_generator = _ip_gen()
- # throw away first network address
- self.next_ip_address()
-
- self.self_ip = self_ip
- if fixed_ip:
- self.ip_addr = fixed_ip
- else:
- self.ip_addr = self.next_ip_address()
- if not reuse:
- def f():
- if self.br_type == BRIDGE_TYPE_DOCKER:
- gw = "--gateway %s" % self.ip_addr.split('/')[0]
- v6 = ''
- if self.subnet.version == 6:
- v6 = '--ipv6'
- cmd = ("docker network create --driver bridge %s "
- "%s --subnet %s %s" % (v6, gw, subnet, self.name))
- elif self.br_type == BRIDGE_TYPE_BRCTL:
- cmd = "ip link add {0} type bridge".format(self.name)
- elif self.br_type == BRIDGE_TYPE_OVS:
- cmd = "ovs-vsctl add-br {0}".format(self.name)
- else:
- raise ValueError('Unsupported br_type: %s' % self.br_type)
- self.delete()
- self.execute(cmd, sudo=True, retry=True)
- try_several_times(f)
- if not self.docker_nw:
- self.execute("ip link set up dev {0}".format(self.name),
- sudo=True, retry=True)
-
- if not self.docker_nw and self_ip:
- ips = self.check_br_addr(self.name)
- for key, ip in ips.items():
- if self.subnet.version == key:
- self.execute(
- "ip addr del {0} dev {1}".format(ip, self.name),
- sudo=True, retry=True)
- self.execute(
- "ip addr add {0} dev {1}".format(self.ip_addr, self.name),
- sudo=True, retry=True)
- self.ctns = []
-
- def get_bridges_dc(self):
- out = self.execute('docker network ls', sudo=True, retry=True)
- bridges = []
- for line in out.splitlines()[1:]:
- bridges.append(line.split()[1])
- return bridges
-
- def get_bridges_brctl(self):
- out = self.execute('brctl show', retry=True)
- bridges = []
- for line in out.splitlines()[1:]:
- bridges.append(line.split()[0])
- return bridges
-
- def get_bridges_ovs(self):
- out = self.execute('ovs-vsctl list-br', sudo=True, retry=True)
- return out.splitlines()
-
- def get_bridges(self):
- if self.br_type == BRIDGE_TYPE_DOCKER:
- return self.get_bridges_dc()
- elif self.br_type == BRIDGE_TYPE_BRCTL:
- return self.get_bridges_brctl()
- elif self.br_type == BRIDGE_TYPE_OVS:
- return self.get_bridges_ovs()
-
- def exist(self):
- return self.name in self.get_bridges()
-
- def execute(self, cmd, capture=True, sudo=False, retry=False):
- if sudo:
- m = self.cmd.sudo
- else:
- m = self.cmd.execute
- if retry:
- return m(cmd, capture=capture, try_times=3, interval=1)
- else:
- return m(cmd, capture=capture)
-
- def check_br_addr(self, br):
- ips = {}
- cmd = "ip a show dev %s" % br
- for line in self.execute(cmd, sudo=True).split('\n'):
- if line.strip().startswith("inet "):
- elems = [e.strip() for e in line.strip().split(' ')]
- ips[4] = elems[1]
- elif line.strip().startswith("inet6 "):
- elems = [e.strip() for e in line.strip().split(' ')]
- ips[6] = elems[1]
- return ips
-
- def next_ip_address(self):
- return "{0}/{1}".format(next(self._ip_generator),
- self.subnet.prefixlen)
-
- def addif(self, ctn):
- name = ctn.next_if_name()
- self.ctns.append(ctn)
- ip_address = None
- if self.docker_nw:
- ipv4 = None
- ipv6 = None
- ip_address = self.next_ip_address()
- ip_address_ip = ip_address.split('/')[0]
- version = 4
- if netaddr.IPNetwork(ip_address).version == 6:
- version = 6
- opt_ip = "--ip %s" % ip_address_ip
- if version == 4:
- ipv4 = ip_address
- else:
- opt_ip = "--ip6 %s" % ip_address_ip
- ipv6 = ip_address
- cmd = "docker network connect %s %s %s" % (
- opt_ip, self.name, ctn.docker_name())
- self.execute(cmd, sudo=True)
- ctn.set_addr_info(bridge=self.name, ipv4=ipv4, ipv6=ipv6,
- ifname=name)
- else:
- if self.with_ip:
- ip_address = self.next_ip_address()
- version = 4
- if netaddr.IPNetwork(ip_address).version == 6:
- version = 6
- ctn.pipework(self, ip_address, name, version=version)
- else:
- ctn.pipework(self, '0/0', name)
- return ip_address
-
- def delete(self, check_exist=True):
- if check_exist:
- if not self.exist():
- return
- if self.br_type == BRIDGE_TYPE_DOCKER:
- self.execute("docker network rm %s" % self.name,
- sudo=True, retry=True)
- elif self.br_type == BRIDGE_TYPE_BRCTL:
- self.execute("ip link set down dev %s" % self.name,
- sudo=True, retry=True)
- self.execute(
- "ip link delete %s type bridge" % self.name,
- sudo=True, retry=True)
- elif self.br_type == BRIDGE_TYPE_OVS:
- self.execute(
- "ovs-vsctl del-br %s" % self.name,
- sudo=True, retry=True)
-
-
-class Container(object):
- def __init__(self, name, image=None):
- self.name = name
- self.image = image
- self.shared_volumes = []
- self.ip_addrs = []
- self.ip6_addrs = []
- self.is_running = False
- self.eths = []
- self.id = None
-
- self.cmd = Command()
- self.remove()
-
- def docker_name(self):
- if TEST_PREFIX == DEFAULT_TEST_PREFIX:
- return self.name
- return '{0}_{1}'.format(TEST_PREFIX, self.name)
-
- def get_docker_id(self):
- if self.id:
- return self.id
- else:
- return self.docker_name()
-
- def next_if_name(self):
- name = 'eth{0}'.format(len(self.eths) + 1)
- self.eths.append(name)
- return name
-
- def set_addr_info(self, bridge, ipv4=None, ipv6=None, ifname='eth0'):
- if ipv4:
- self.ip_addrs.append((ifname, ipv4, bridge))
- if ipv6:
- self.ip6_addrs.append((ifname, ipv6, bridge))
-
- def get_addr_info(self, bridge, ipv=4):
- addrinfo = {}
- if ipv == 4:
- ip_addrs = self.ip_addrs
- elif ipv == 6:
- ip_addrs = self.ip6_addrs
- else:
- return None
- for addr in ip_addrs:
- if addr[2] == bridge:
- addrinfo[addr[1]] = addr[0]
- return addrinfo
-
- def execute(self, cmd, capture=True, sudo=False, retry=False):
- if sudo:
- m = self.cmd.sudo
- else:
- m = self.cmd.execute
- if retry:
- return m(cmd, capture=capture, try_times=3, interval=1)
- else:
- return m(cmd, capture=capture)
-
- def dcexec(self, cmd, capture=True, retry=False):
- if retry:
- return self.cmd.sudo(cmd, capture=capture, try_times=3, interval=1)
- else:
- return self.cmd.sudo(cmd, capture=capture)
-
- def exec_on_ctn(self, cmd, capture=True, detach=False):
- name = self.docker_name()
- flag = '-d' if detach else ''
- return self.dcexec('docker exec {0} {1} {2}'.format(
- flag, name, cmd), capture=capture)
-
- def get_containers(self, allctn=False):
- cmd = 'docker ps --no-trunc=true'
- if allctn:
- cmd += ' --all=true'
- out = self.dcexec(cmd, retry=True)
- containers = []
- for line in out.splitlines()[1:]:
- containers.append(line.split()[-1])
- return containers
-
- def exist(self, allctn=False):
- return self.docker_name() in self.get_containers(allctn=allctn)
-
- 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} --hostname {0} -id {1}".format(self.docker_name(),
- self.image)
- self.id = self.dcexec(str(c), retry=True)
- self.is_running = True
- self.exec_on_ctn("ip li set up dev lo")
- ipv4 = None
- ipv6 = None
- for line in self.exec_on_ctn("ip a show dev eth0").split('\n'):
- if line.strip().startswith("inet "):
- elems = [e.strip() for e in line.strip().split(' ')]
- ipv4 = elems[1]
- elif line.strip().startswith("inet6 "):
- elems = [e.strip() for e in line.strip().split(' ')]
- ipv6 = elems[1]
- self.set_addr_info(bridge='docker0', ipv4=ipv4, ipv6=ipv6,
- ifname='eth0')
- return 0
-
- def stop(self, check_exist=True):
- if check_exist:
- if not self.exist(allctn=False):
- return
- ctn_id = self.get_docker_id()
- out = self.dcexec('docker stop -t 0 %s' % ctn_id, retry=True)
- self.is_running = False
- return out
-
- def remove(self, check_exist=True):
- if check_exist:
- if not self.exist(allctn=True):
- return
- ctn_id = self.get_docker_id()
- out = self.dcexec('docker rm -f %s' % ctn_id, retry=True)
- self.is_running = False
- return out
-
- def pipework(self, bridge, ip_addr, intf_name="", version=4):
- if not self.is_running:
- LOG.warning('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"
- ipv4 = None
- ipv6 = None
- if version == 4:
- ipv4 = ip_addr
- else:
- c << '-a 6'
- ipv6 = ip_addr
- c << "{0} {1}".format(self.docker_name(), ip_addr)
- self.set_addr_info(bridge=bridge.name, ipv4=ipv4, ipv6=ipv6,
- ifname=intf_name)
- self.execute(str(c), sudo=True, retry=True)
-
- def get_pid(self):
- if self.is_running:
- cmd = "docker inspect -f '{{.State.Pid}}' %s" % self.docker_name()
- return int(self.dcexec(cmd))
- return -1
-
- def start_tcpdump(self, interface=None, filename=None):
- if not interface:
- interface = "eth0"
- if not filename:
- filename = "{0}/{1}.dump".format(
- self.shared_volumes[0][1], interface)
- self.exec_on_ctn(
- "tcpdump -i {0} -w {1}".format(interface, filename),
- detach=True)
-
-
-class BGPContainer(Container):
-
- WAIT_FOR_BOOT = 1
- RETRY_INTERVAL = 5
- DEFAULT_PEER_ARG = {'neigh_addr': '',
- 'passwd': None,
- 'vpn': False,
- 'flowspec': False,
- 'is_rs_client': False,
- 'is_rr_client': False,
- 'cluster_id': None,
- 'policies': None,
- 'passive': False,
- 'local_addr': '',
- 'as2': False,
- 'graceful_restart': None,
- 'local_as': None,
- 'prefix_limit': None}
- default_peer_keys = sorted(DEFAULT_PEER_ARG.keys())
- DEFAULT_ROUTE_ARG = {'prefix': None,
- 'rf': 'ipv4',
- 'attr': None,
- 'next-hop': None,
- 'as-path': None,
- 'community': None,
- 'med': None,
- 'local-pref': None,
- 'extended-community': None,
- 'matchs': None,
- 'thens': None}
- default_route_keys = sorted(DEFAULT_ROUTE_ARG.keys())
-
- def __init__(self, name, asn, router_id, ctn_image_name=None):
- self.config_dir = TEST_BASE_DIR
- if TEST_PREFIX:
- self.config_dir = os.path.join(self.config_dir, TEST_PREFIX)
- self.config_dir = os.path.join(self.config_dir, name)
- self.asn = asn
- self.router_id = router_id
- self.peers = {}
- self.routes = {}
- self.policies = {}
- super(BGPContainer, self).__init__(name, ctn_image_name)
- self.execute(
- 'rm -rf {0}'.format(self.config_dir), sudo=True)
- self.execute('mkdir -p {0}'.format(self.config_dir))
- self.execute('chmod 777 {0}'.format(self.config_dir))
-
- def __repr__(self):
- return str({'name': self.name, 'asn': self.asn,
- 'router_id': self.router_id})
-
- def run(self, wait=False, w_time=WAIT_FOR_BOOT):
- self.create_config()
- super(BGPContainer, self).run()
- if wait:
- time.sleep(w_time)
- return w_time
-
- def add_peer(self, peer, bridge='', reload_config=True, v6=False,
- peer_info=None):
- peer_info = peer_info or {}
- self.peers[peer] = self.DEFAULT_PEER_ARG.copy()
- self.peers[peer].update(peer_info)
- peer_keys = sorted(self.peers[peer].keys())
- if peer_keys != self.default_peer_keys:
- raise Exception("argument error peer_info: %s" % peer_info)
-
- neigh_addr = ''
- local_addr = ''
- it = itertools.product(self.ip_addrs, peer.ip_addrs)
- if v6:
- it = itertools.product(self.ip6_addrs, peer.ip6_addrs)
-
- for me, you in it:
- if bridge != '' and bridge != me[2]:
- continue
- if me[2] == you[2]:
- neigh_addr = you[1]
- local_addr = me[1]
- if v6:
- addr, mask = local_addr.split('/')
- local_addr = "{0}%{1}/{2}".format(addr, me[0], mask)
- break
-
- if neigh_addr == '':
- raise Exception('peer {0} seems not ip reachable'.format(peer))
-
- if not self.peers[peer]['policies']:
- self.peers[peer]['policies'] = {}
-
- self.peers[peer]['neigh_addr'] = neigh_addr
- self.peers[peer]['local_addr'] = local_addr
- if self.is_running and reload_config:
- self.create_config()
- self.reload_config()
-
- def del_peer(self, peer, reload_config=True):
- del self.peers[peer]
- if self.is_running and reload_config:
- self.create_config()
- self.reload_config()
-
- def disable_peer(self, peer):
- raise NotImplementedError()
-
- def enable_peer(self, peer):
- raise NotImplementedError()
-
- def log(self):
- return self.execute('cat {0}/*.log'.format(self.config_dir))
-
- def add_route(self, route, reload_config=True, route_info=None):
- route_info = route_info or {}
- self.routes[route] = self.DEFAULT_ROUTE_ARG.copy()
- self.routes[route].update(route_info)
- route_keys = sorted(self.routes[route].keys())
- if route_keys != self.default_route_keys:
- raise Exception("argument error route_info: %s" % route_info)
- self.routes[route]['prefix'] = route
- if self.is_running and reload_config:
- self.create_config()
- self.reload_config()
-
- def add_policy(self, policy, peer, typ, default='accept',
- reload_config=True):
- self.set_default_policy(peer, typ, default)
- self.define_policy(policy)
- self.assign_policy(peer, policy, typ)
- if self.is_running and reload_config:
- self.create_config()
- self.reload_config()
-
- def set_default_policy(self, peer, typ, default):
- if (typ in ['in', 'out', 'import', 'export'] and
- default in ['reject', 'accept']):
- if 'default-policy' not in self.peers[peer]:
- self.peers[peer]['default-policy'] = {}
- self.peers[peer]['default-policy'][typ] = default
- else:
- raise Exception('wrong type or default')
-
- def define_policy(self, policy):
- self.policies[policy['name']] = policy
-
- def assign_policy(self, peer, policy, typ):
- if peer not in self.peers:
- raise Exception('peer {0} not found'.format(peer.name))
- name = policy['name']
- if name not in self.policies:
- raise Exception('policy {0} not found'.format(name))
- self.peers[peer]['policies'][typ] = policy
-
- def get_local_rib(self, peer, rf):
- raise NotImplementedError()
-
- def get_global_rib(self, rf):
- raise NotImplementedError()
-
- def get_neighbor_state(self, peer_id):
- raise NotImplementedError()
-
- def get_reachablily(self, prefix, timeout=20):
- version = netaddr.IPNetwork(prefix).version
- addr = prefix.split('/')[0]
- if version == 4:
- ping_cmd = 'ping'
- elif version == 6:
- ping_cmd = 'ping6'
- else:
- raise Exception(
- 'unsupported route family: {0}'.format(version))
- cmd = '/bin/bash -c "/bin/{0} -c 1 -w 1 {1} | xargs echo"'.format(
- ping_cmd, addr)
- interval = 1
- count = 0
- while True:
- res = self.exec_on_ctn(cmd)
- LOG.info(res)
- if '1 packets received' in res and '0% packet loss':
- break
- time.sleep(interval)
- count += interval
- if count >= timeout:
- raise Exception('timeout')
- return True
-
- def wait_for(self, expected_state, peer, timeout=120):
- interval = 1
- count = 0
- while True:
- state = self.get_neighbor_state(peer)
- LOG.info("%s's peer %s state: %s",
- self.router_id, peer.router_id, state)
- if state == expected_state:
- return
-
- time.sleep(interval)
- count += interval
- if count >= timeout:
- raise Exception('timeout')
-
- def add_static_route(self, network, next_hop):
- cmd = '/sbin/ip route add {0} via {1}'.format(network, next_hop)
- self.exec_on_ctn(cmd)
-
- def set_ipv6_forward(self):
- cmd = 'sysctl -w net.ipv6.conf.all.forwarding=1'
- self.exec_on_ctn(cmd)
-
- def create_config(self):
- raise NotImplementedError()
-
- def reload_config(self):
- raise NotImplementedError()
diff --git a/ryu/tests/integrated/common/install_docker_test_pkg.sh b/ryu/tests/integrated/common/install_docker_test_pkg.sh
deleted file mode 100644
index a771dfc..0000000
--- a/ryu/tests/integrated/common/install_docker_test_pkg.sh
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/bin/bash
-set -ex
-
-RYU_PATH=`dirname $0`
-
-source ${RYU_PATH}/install_docker_test_pkg_common.sh
-
-function add_docker_aptline {
- sudo apt-get update
- if ! apt-cache search docker-engine | grep docker-engine; then
- VER=`lsb_release -r`
- if echo $VER | grep 12.04; then
- REL_NAME=precise
- elif echo $VER | grep 14.04; then
- REL_NAME=trusty
- elif echo $VER | grep 15.10; then
- REL_NAME=wily
- elif echo $VER | grep 16.04; then
- REL_NAME=xenial
- else
- retrun 1
- fi
- RELEASE=ubuntu-$REL_NAME
- sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
- sudo sh -c "echo deb https://apt.dockerproject.org/repo $RELEASE main > /etc/apt/sources.list.d/docker.list"
- fi
-}
-
-init_variables
-process_options "$@"
-
-if [ $APTLINE_DOCKER -eq 1 ]; then
- add_docker_aptline
-fi
-
-sudo apt-get update
-if apt-cache search docker-engine | grep docker-engine; then
- DOCKER_PKG=docker-engine
-else
- DOCKER_PKG=docker.io
-fi
-sudo apt-get install -y $DOCKER_PKG
-install_depends_pkg
diff --git a/ryu/tests/integrated/common/install_docker_test_pkg_common.sh b/ryu/tests/integrated/common/install_docker_test_pkg_common.sh
deleted file mode 100644
index 44a3e10..0000000
--- a/ryu/tests/integrated/common/install_docker_test_pkg_common.sh
+++ /dev/null
@@ -1,39 +0,0 @@
-#!/bin/bash
-set -ex
-
-function init_variables {
- APTLINE_DOCKER=0
- DIR_BASE=/tmp
-}
-
-function process_options {
- local max
- local i
- max=$#
- i=1
- while [ $i -le $max ]; do
- case "$1" in
- -a|--add-docker-aptline)
- APTLINE_DOCKER=1
- ;;
- -d|--download-dir)
- shift; ((i++))
- DIR_BASE=$1
- ;;
- esac
- shift; ((i++))
- done
-}
-
-function install_pipework {
- if ! which /usr/local/bin/pipework >/dev/null
- then
- sudo rm -rf $DIR_BASE/pipework
- git clone https://github.com/jpetazzo/pipework.git $DIR_BASE/pipework
- sudo install -m 0755 $DIR_BASE/pipework/pipework /usr/local/bin/pipework
- fi
-}
-
-function install_depends_pkg {
- install_pipework
-}
diff --git a/ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh b/ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
deleted file mode 100644
index d8c3b49..0000000
--- a/ryu/tests/integrated/common/install_docker_test_pkg_for_travis.sh
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-set -ex
-
-RYU_PATH=`dirname $0`
-
-source ${RYU_PATH}/install_docker_test_pkg_common.sh
-
-init_variables
-process_options "$@"
-
-sudo apt-get update
-install_depends_pkg
diff --git a/ryu/tests/integrated/common/quagga.py b/ryu/tests/integrated/common/quagga.py
deleted file mode 100644
index 9b6d218..0000000
--- a/ryu/tests/integrated/common/quagga.py
+++ /dev/null
@@ -1,332 +0,0 @@
-# Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
-#
-# This is based on the following
-# https://github.com/osrg/gobgp/test/lib/quagga.py
-#
-# 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 __future__ import absolute_import
-
-import logging
-import os
-
-import netaddr
-
-from . import docker_base as base
-
-LOG = logging.getLogger(__name__)
-
-
-class QuaggaBGPContainer(base.BGPContainer):
-
- WAIT_FOR_BOOT = 1
- SHARED_VOLUME = '/etc/quagga'
-
- def __init__(self, name, asn, router_id, ctn_image_name, zebra=False):
- super(QuaggaBGPContainer, self).__init__(name, asn, router_id,
- ctn_image_name)
- self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
- self.zebra = zebra
- self._create_config_debian()
-
- def run(self, wait=False, w_time=WAIT_FOR_BOOT):
- w_time = super(QuaggaBGPContainer,
- self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
- return w_time
-
- def get_global_rib(self, prefix='', rf='ipv4'):
- rib = []
- if prefix != '':
- return self.get_global_rib_with_prefix(prefix, rf)
-
- out = self.vtysh('show bgp {0} unicast'.format(rf), config=False)
- if out.startswith('No BGP network exists'):
- return rib
-
- read_next = False
-
- for line in out.split('\n'):
- ibgp = False
- if line[:2] == '*>':
- line = line[2:]
- if line[0] == 'i':
- line = line[1:]
- ibgp = True
- elif not read_next:
- continue
-
- elems = line.split()
-
- if len(elems) == 1:
- read_next = True
- prefix = elems[0]
- continue
- elif read_next:
- nexthop = elems[0]
- else:
- prefix = elems[0]
- nexthop = elems[1]
- read_next = False
-
- rib.append({'prefix': prefix, 'nexthop': nexthop,
- 'ibgp': ibgp})
-
- return rib
-
- def get_global_rib_with_prefix(self, prefix, rf):
- rib = []
-
- lines = [line.strip() for line in self.vtysh(
- 'show bgp {0} unicast {1}'.format(rf, prefix),
- config=False).split('\n')]
-
- if lines[0] == '% Network not in table':
- return rib
-
- lines = lines[2:]
-
- if lines[0].startswith('Not advertised'):
- lines.pop(0) # another useless line
- elif lines[0].startswith('Advertised to non peer-group peers:'):
- lines = lines[2:] # other useless lines
- else:
- raise Exception('unknown output format {0}'.format(lines))
-
- if lines[0] == 'Local':
- aspath = []
- else:
- aspath = [int(asn) for asn in lines[0].split()]
-
- nexthop = lines[1].split()[0].strip()
- info = [s.strip(',') for s in lines[2].split()]
- attrs = []
- if 'metric' in info:
- med = info[info.index('metric') + 1]
- attrs.append({'type': base.BGP_ATTR_TYPE_MULTI_EXIT_DISC,
- 'metric': int(med)})
- if 'localpref' in info:
- localpref = info[info.index('localpref') + 1]
- attrs.append({'type': base.BGP_ATTR_TYPE_LOCAL_PREF,
- 'value': int(localpref)})
-
- rib.append({'prefix': prefix, 'nexthop': nexthop,
- 'aspath': aspath, 'attrs': attrs})
-
- return rib
-
- def get_neighbor_state(self, peer):
- if peer not in self.peers:
- raise Exception('not found peer {0}'.format(peer.router_id))
-
- neigh_addr = self.peers[peer]['neigh_addr'].split('/')[0]
-
- info = [l.strip() for l in self.vtysh(
- 'show bgp neighbors {0}'.format(neigh_addr),
- config=False).split('\n')]
-
- if not info[0].startswith('BGP neighbor is'):
- raise Exception('unknown format')
-
- idx1 = info[0].index('BGP neighbor is ')
- idx2 = info[0].index(',')
- n_addr = info[0][idx1 + len('BGP neighbor is '):idx2]
- if n_addr == neigh_addr:
- idx1 = info[2].index('= ')
- state = info[2][idx1 + len('= '):]
- if state.startswith('Idle'):
- return base.BGP_FSM_IDLE
- elif state.startswith('Active'):
- return base.BGP_FSM_ACTIVE
- elif state.startswith('Established'):
- return base.BGP_FSM_ESTABLISHED
- else:
- return state
-
- raise Exception('not found peer {0}'.format(peer.router_id))
-
- def send_route_refresh(self):
- self.vtysh('clear ip bgp * soft', config=False)
-
- def create_config(self):
- zebra = 'no'
- self._create_config_bgp()
- if self.zebra:
- zebra = 'yes'
- self._create_config_zebra()
- self._create_config_daemons(zebra)
-
- def _create_config_debian(self):
- c = base.CmdBuffer()
- c << 'vtysh_enable=yes'
- c << 'zebra_options=" --daemon -A 127.0.0.1"'
- c << 'bgpd_options=" --daemon -A 127.0.0.1"'
- c << 'ospfd_options=" --daemon -A 127.0.0.1"'
- c << 'ospf6d_options=" --daemon -A ::1"'
- c << 'ripd_options=" --daemon -A 127.0.0.1"'
- c << 'ripngd_options=" --daemon -A ::1"'
- c << 'isisd_options=" --daemon -A 127.0.0.1"'
- c << 'babeld_options=" --daemon -A 127.0.0.1"'
- c << 'watchquagga_enable=yes'
- c << 'watchquagga_options=(--daemon)'
- with open('{0}/debian.conf'.format(self.config_dir), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def _create_config_daemons(self, zebra='no'):
- c = base.CmdBuffer()
- c << 'zebra=%s' % zebra
- c << 'bgpd=yes'
- c << 'ospfd=no'
- c << 'ospf6d=no'
- c << 'ripd=no'
- c << 'ripngd=no'
- c << 'isisd=no'
- c << 'babeld=no'
- with open('{0}/daemons'.format(self.config_dir), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def _create_config_bgp(self):
-
- c = base.CmdBuffer()
- c << 'hostname bgpd'
- c << 'password zebra'
- c << 'router bgp {0}'.format(self.asn)
- c << 'bgp router-id {0}'.format(self.router_id)
- if any(info['graceful_restart'] for info in self.peers.values()):
- c << 'bgp graceful-restart'
-
- version = 4
- for peer, info in self.peers.items():
- version = netaddr.IPNetwork(info['neigh_addr']).version
- n_addr = info['neigh_addr'].split('/')[0]
- if version == 6:
- c << 'no bgp default ipv4-unicast'
-
- c << 'neighbor {0} remote-as {1}'.format(n_addr, peer.asn)
- if info['is_rs_client']:
- c << 'neighbor {0} route-server-client'.format(n_addr)
- for typ, p in info['policies'].items():
- c << 'neighbor {0} route-map {1} {2}'.format(n_addr, p['name'],
- typ)
- if info['passwd']:
- c << 'neighbor {0} password {1}'.format(n_addr, info['passwd'])
- if info['passive']:
- c << 'neighbor {0} passive'.format(n_addr)
- if version == 6:
- c << 'address-family ipv6 unicast'
- c << 'neighbor {0} activate'.format(n_addr)
- c << 'exit-address-family'
-
- for route in self.routes.values():
- 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'
- c << 'redistribute connected'
- c << 'exit-address-family'
- else:
- c << 'redistribute connected'
-
- for name, policy in self.policies.items():
- c << 'access-list {0} {1} {2}'.format(name, policy['type'],
- policy['match'])
- c << 'route-map {0} permit 10'.format(name)
- c << 'match ip address {0}'.format(name)
- c << 'set metric {0}'.format(policy['med'])
-
- c << 'debug bgp as4'
- c << 'debug bgp fsm'
- c << 'debug bgp updates'
- c << 'debug bgp events'
- c << 'log file {0}/bgpd.log'.format(self.SHARED_VOLUME)
-
- with open('{0}/bgpd.conf'.format(self.config_dir), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def _create_config_zebra(self):
- c = base.CmdBuffer()
- c << 'hostname zebra'
- c << 'password zebra'
- c << 'log file {0}/zebra.log'.format(self.SHARED_VOLUME)
- c << 'debug zebra packet'
- c << 'debug zebra kernel'
- c << 'debug zebra rib'
- c << ''
-
- with open('{0}/zebra.conf'.format(self.config_dir), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def vtysh(self, cmd, config=True):
- if not isinstance(cmd, list):
- cmd = [cmd]
- cmd = ' '.join("-c '{0}'".format(c) for c in cmd)
- if config:
- return self.exec_on_ctn(
- "vtysh -d bgpd -c 'en' -c 'conf t' -c "
- "'router bgp {0}' {1}".format(self.asn, cmd),
- capture=True)
- else:
- return self.exec_on_ctn("vtysh -d bgpd {0}".format(cmd),
- capture=True)
-
- def reload_config(self):
- daemon = []
- daemon.append('bgpd')
- if self.zebra:
- daemon.append('zebra')
- for d in daemon:
- cmd = '/usr/bin/pkill {0} -SIGHUP'.format(d)
- self.exec_on_ctn(cmd, capture=True)
-
-
-class RawQuaggaBGPContainer(QuaggaBGPContainer):
- def __init__(self, name, config, ctn_image_name,
- zebra=False):
- asn = None
- router_id = None
- for line in config.split('\n'):
- line = line.strip()
- if line.startswith('router bgp'):
- asn = int(line[len('router bgp'):].strip())
- if line.startswith('bgp router-id'):
- router_id = line[len('bgp router-id'):].strip()
- if not asn:
- raise Exception('asn not in quagga config')
- if not router_id:
- raise Exception('router-id not in quagga config')
- self.config = config
- super(RawQuaggaBGPContainer, self).__init__(name, asn, router_id,
- ctn_image_name, zebra)
-
- def create_config(self):
- with open(os.path.join(self.config_dir, 'bgpd.conf'), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(self.config)
- f.writelines(self.config)
diff --git a/ryu/tests/integrated/common/ryubgp.py b/ryu/tests/integrated/common/ryubgp.py
deleted file mode 100644
index 8fe16f4..0000000
--- a/ryu/tests/integrated/common/ryubgp.py
+++ /dev/null
@@ -1,212 +0,0 @@
-# Copyright (C) 2016 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 __future__ import absolute_import
-
-import logging
-import os
-import time
-
-from . import docker_base as base
-
-LOG = logging.getLogger(__name__)
-
-
-class RyuBGPContainer(base.BGPContainer):
-
- WAIT_FOR_BOOT = 1
- SHARED_VOLUME = '/etc/ryu'
-
- def __init__(self, name, asn, router_id, ctn_image_name):
- super(RyuBGPContainer, self).__init__(name, asn, router_id,
- ctn_image_name)
- self.RYU_CONF = os.path.join(self.config_dir, 'ryu.conf')
- self.SHARED_RYU_CONF = os.path.join(self.SHARED_VOLUME, 'ryu.conf')
- self.SHARED_BGP_CONF = os.path.join(self.SHARED_VOLUME, 'bgp_conf.py')
- self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
-
- def _create_config_ryu(self):
- c = base.CmdBuffer()
- c << '[DEFAULT]'
- c << 'verbose=True'
- c << 'log_file=/etc/ryu/manager.log'
- with open(self.RYU_CONF, 'w') as f:
- LOG.info("[%s's new config]" % self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def _create_config_ryu_bgp(self):
- c = base.CmdBuffer()
- c << 'import os'
- c << ''
- c << 'BGP = {'
- c << " 'local_as': %s," % str(self.asn)
- c << " 'router_id': '%s'," % self.router_id
- c << " 'neighbors': ["
- c << " {"
- for peer, info in self.peers.items():
- n_addr = info['neigh_addr'].split('/')[0]
- c << " 'address': '%s'," % n_addr
- c << " 'remote_as': %s," % str(peer.asn)
- c << " 'enable_ipv4': True,"
- c << " 'enable_ipv6': True,"
- c << " 'enable_vpnv4': True,"
- c << " 'enable_vpnv6': True,"
- c << ' },'
- c << ' ],'
- c << " 'routes': ["
- for route in self.routes.values():
- c << " {"
- c << " 'prefix': '%s'," % route['prefix']
- c << " },"
- c << " ],"
- c << "}"
- log_conf = """LOGGING = {
-
- # We use python logging package for logging.
- 'version': 1,
- 'disable_existing_loggers': False,
-
- 'formatters': {
- 'verbose': {
- 'format': '%(levelname)s %(asctime)s %(module)s ' +
- '[%(process)d %(thread)d] %(message)s'
- },
- 'simple': {
- 'format': '%(levelname)s %(asctime)s %(module)s %(lineno)s ' +
- '%(message)s'
- },
- 'stats': {
- 'format': '%(message)s'
- },
- },
-
- 'handlers': {
- # Outputs log to console.
- 'console': {
- 'level': 'DEBUG',
- 'class': 'logging.StreamHandler',
- 'formatter': 'simple'
- },
- 'console_stats': {
- 'level': 'DEBUG',
- 'class': 'logging.StreamHandler',
- 'formatter': 'stats'
- },
- # Rotates log file when its size reaches 10MB.
- 'log_file': {
- 'level': 'DEBUG',
- 'class': 'logging.handlers.RotatingFileHandler',
- 'filename': os.path.join('.', 'bgpspeaker.log'),
- 'maxBytes': '10000000',
- 'formatter': 'verbose'
- },
- 'stats_file': {
- 'level': 'DEBUG',
- 'class': 'logging.handlers.RotatingFileHandler',
- 'filename': os.path.join('.', 'statistics_bgps.log'),
- 'maxBytes': '10000000',
- 'formatter': 'stats'
- },
- },
-
- # Fine-grained control of logging per instance.
- 'loggers': {
- 'bgpspeaker': {
- 'handlers': ['console', 'log_file'],
- 'handlers': ['console'],
- 'level': 'DEBUG',
- 'propagate': False,
- },
- 'stats': {
- 'handlers': ['stats_file', 'console_stats'],
- 'level': 'INFO',
- 'propagate': False,
- 'formatter': 'stats',
- },
- },
-
- # Root loggers.
- 'root': {
- 'handlers': ['console', 'log_file'],
- 'level': 'DEBUG',
- 'propagate': True,
- },
-}"""
- c << log_conf
- with open(os.path.join(self.config_dir, 'bgp_conf.py'), 'w') as f:
- LOG.info("[%s's new config]", self.name)
- LOG.info(str(c))
- f.writelines(str(c))
-
- def create_config(self):
- self._create_config_ryu()
- self._create_config_ryu_bgp()
-
- def is_running_ryu(self):
- results = self.exec_on_ctn('ps ax')
- running = False
- for line in results.split('\n')[1:]:
- if 'ryu-manager' in line:
- running = True
- return running
-
- def start_ryubgp(self, check_running=True, retry=False):
- if check_running:
- if self.is_running_ryu():
- return True
- result = False
- if retry:
- try_times = 3
- else:
- try_times = 1
- cmd = "ryu-manager --verbose "
- cmd += "--config-file %s " % self.SHARED_RYU_CONF
- cmd += "--bgp-app-config-file %s " % self.SHARED_BGP_CONF
- cmd += "ryu.services.protocols.bgp.application"
- for _ in range(try_times):
- self.exec_on_ctn(cmd, detach=True)
- if self.is_running_ryu():
- result = True
- break
- time.sleep(1)
- return result
-
- def stop_ryubgp(self, check_running=True, retry=False):
- if check_running:
- if not self.is_running_ryu():
- return True
- result = False
- if retry:
- try_times = 3
- else:
- try_times = 1
- for _ in range(try_times):
- cmd = '/usr/bin/pkill ryu-manager -SIGTERM'
- self.exec_on_ctn(cmd)
- if not self.is_running_ryu():
- result = True
- break
- time.sleep(1)
- return result
-
- def run(self, wait=False, w_time=WAIT_FOR_BOOT):
- w_time = super(RyuBGPContainer,
- self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
- return w_time
-
- def reload_config(self):
- self.stop_ryubgp(retry=True)
- self.start_ryubgp(retry=True)
diff --git a/tests/integrated/bgp/base.py b/tests/integrated/bgp/base.py
index 26fa396..2f210de 100644
--- a/tests/integrated/bgp/base.py
+++ b/tests/integrated/bgp/base.py
@@ -20,9 +20,9 @@ import logging
import sys
import unittest
-from ryu.tests.integrated.common import docker_base as ctn_base
-from ryu.tests.integrated.common import ryubgp
-from ryu.tests.integrated.common import quagga
+from ryu.lib.docker import docker_base as ctn_base
+from ryu.lib.docker import ryubgp
+from ryu.lib.docker import quagga
LOG = logging.getLogger(__name__)
diff --git a/tests/integrated/bgp/base_ip6.py b/tests/integrated/bgp/base_ip6.py
index be26faf..d867920 100644
--- a/tests/integrated/bgp/base_ip6.py
+++ b/tests/integrated/bgp/base_ip6.py
@@ -20,9 +20,9 @@ import logging
import sys
import unittest
-from ryu.tests.integrated.common import docker_base as ctn_base
-from ryu.tests.integrated.common import ryubgp
-from ryu.tests.integrated.common import quagga
+from ryu.lib.docker import docker_base as ctn_base
+from ryu.lib.docker import ryubgp
+from ryu.lib.docker import quagga
LOG = logging.getLogger(__name__)
diff --git a/tests/integrated/bgp/test_basic.py b/tests/integrated/bgp/test_basic.py
index 5817d44..d1eda39 100644
--- a/tests/integrated/bgp/test_basic.py
+++ b/tests/integrated/bgp/test_basic.py
@@ -18,7 +18,7 @@ from __future__ import absolute_import
import time
-from ryu.tests.integrated.common import docker_base as ctn_base
+from ryu.lib.docker import docker_base as ctn_base
from . import base
diff --git a/tests/integrated/bgp/test_ip6_basic.py b/tests/integrated/bgp/test_ip6_basic.py
index 40461a5..911a0b5 100644
--- a/tests/integrated/bgp/test_ip6_basic.py
+++ b/tests/integrated/bgp/test_ip6_basic.py
@@ -18,7 +18,7 @@ from __future__ import absolute_import
import time
-from ryu.tests.integrated.common import docker_base as ctn_base
+from ryu.lib.docker import docker_base as ctn_base
from . import base_ip6 as base
--
2.7.4
2.7.4