summaryrefslogtreecommitdiffhomepage
path: root/tests/integrated/common/docker_base.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/integrated/common/docker_base.py')
-rw-r--r--tests/integrated/common/docker_base.py801
1 files changed, 801 insertions, 0 deletions
diff --git a/tests/integrated/common/docker_base.py b/tests/integrated/common/docker_base.py
new file mode 100644
index 00000000..1ae2cc27
--- /dev/null
+++ b/tests/integrated/common/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()