backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / tests / integrated / common / docker_base.py
1 # Copyright (C) 2015 Nippon Telegraph and Telephone Corporation.
2 #
3 # This is based on the following
4 #     https://github.com/osrg/gobgp/test/lib/base.py
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 #    http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15 # implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18
19 from __future__ import absolute_import
20
21 import itertools
22 import logging
23 import os
24 import subprocess
25 import time
26
27 import netaddr
28 import six
29
30 LOG = logging.getLogger(__name__)
31
32 DEFAULT_TEST_PREFIX = ''
33 DEFAULT_TEST_BASE_DIR = '/tmp/ctn_docker/bgp'
34 TEST_PREFIX = DEFAULT_TEST_PREFIX
35 TEST_BASE_DIR = DEFAULT_TEST_BASE_DIR
36
37 BGP_FSM_IDLE = 'BGP_FSM_IDLE'
38 BGP_FSM_ACTIVE = 'BGP_FSM_ACTIVE'
39 BGP_FSM_ESTABLISHED = 'BGP_FSM_ESTABLISHED'
40
41 BGP_ATTR_TYPE_ORIGIN = 1
42 BGP_ATTR_TYPE_AS_PATH = 2
43 BGP_ATTR_TYPE_NEXT_HOP = 3
44 BGP_ATTR_TYPE_MULTI_EXIT_DISC = 4
45 BGP_ATTR_TYPE_LOCAL_PREF = 5
46 BGP_ATTR_TYPE_COMMUNITIES = 8
47 BGP_ATTR_TYPE_ORIGINATOR_ID = 9
48 BGP_ATTR_TYPE_CLUSTER_LIST = 10
49 BGP_ATTR_TYPE_MP_REACH_NLRI = 14
50 BGP_ATTR_TYPE_EXTENDED_COMMUNITIES = 16
51
52 BRIDGE_TYPE_DOCKER = 'docker'
53 BRIDGE_TYPE_BRCTL = 'brctl'
54 BRIDGE_TYPE_OVS = 'ovs'
55
56
57 class CommandError(Exception):
58     def __init__(self, out):
59         super(CommandError, self).__init__()
60         self.out = out
61
62
63 def try_several_times(f, t=3, s=1):
64     e = RuntimeError()
65     for _ in range(t):
66         try:
67             r = f()
68         except RuntimeError as e:
69             time.sleep(s)
70         else:
71             return r
72     raise e
73
74
75 class CmdBuffer(list):
76     def __init__(self, delim='\n'):
77         super(CmdBuffer, self).__init__()
78         self.delim = delim
79
80     def __lshift__(self, value):
81         self.append(value)
82
83     def __str__(self):
84         return self.delim.join(self)
85
86
87 class CommandOut(str):
88
89     def __new__(cls, stdout, stderr, command, returncode, **kwargs):
90         stdout = stdout or ''
91         obj = super(CommandOut, cls).__new__(cls, stdout, **kwargs)
92         obj.stderr = stderr or ''
93         obj.command = command
94         obj.returncode = returncode
95         return obj
96
97
98 class Command(object):
99
100     def _execute(self, cmd, capture=False, executable=None):
101         """Execute a command using subprocess.Popen()
102         :Parameters:
103             - out: stdout from subprocess.Popen()
104               out has some attributes.
105               out.returncode: returncode of subprocess.Popen()
106               out.stderr: stderr from subprocess.Popen()
107         """
108         if capture:
109             p_stdout = subprocess.PIPE
110             p_stderr = subprocess.PIPE
111         else:
112             p_stdout = None
113             p_stderr = None
114         pop = subprocess.Popen(cmd, shell=True, executable=executable,
115                                stdout=p_stdout,
116                                stderr=p_stderr)
117         __stdout, __stderr = pop.communicate()
118         _stdout = six.text_type(__stdout, 'utf-8')
119         _stderr = six.text_type(__stderr, 'utf-8')
120         out = CommandOut(_stdout, _stderr, cmd, pop.returncode)
121         return out
122
123     def execute(self, cmd, capture=True, try_times=1, interval=1):
124         out = None
125         for i in range(try_times):
126             out = self._execute(cmd, capture=capture)
127             LOG.info(out.command)
128             if out.returncode == 0:
129                 return out
130             LOG.error("stdout: %s", out)
131             LOG.error("stderr: %s", out.stderr)
132             if i + 1 >= try_times:
133                 break
134             time.sleep(interval)
135         raise CommandError(out)
136
137     def sudo(self, cmd, capture=True, try_times=1, interval=1):
138         cmd = 'sudo %s' % cmd
139         return self.execute(cmd, capture=capture,
140                             try_times=try_times, interval=interval)
141
142
143 class DockerImage(object):
144     def __init__(self, baseimage='ubuntu:16.04'):
145         self.baseimage = baseimage
146         self.cmd = Command()
147
148     def get_images(self):
149         out = self.cmd.sudo('sudo docker images')
150         images = []
151         for line in out.splitlines()[1:]:
152             images.append(line.split()[0])
153         return images
154
155     def exist(self, name):
156         return name in self.get_images()
157
158     def build(self, tagname, dockerfile_dir):
159         self.cmd.sudo(
160             "docker build -t {0} {1}".format(tagname, dockerfile_dir),
161             try_times=3)
162
163     def remove(self, tagname, check_exist=False):
164         if check_exist and not self.exist(tagname):
165             return tagname
166         self.cmd.sudo("docker rmi -f %s" % tagname, try_times=3)
167
168     def create_quagga(self, tagname='quagga', image=None, check_exist=False):
169         if check_exist and self.exist(tagname):
170             return tagname
171         workdir = os.path.join(TEST_BASE_DIR, tagname)
172         pkges = ' '.join([
173             'telnet',
174             'tcpdump',
175             'quagga',
176         ])
177         if image:
178             use_image = image
179         else:
180             use_image = self.baseimage
181         c = CmdBuffer()
182         c << 'FROM %s' % use_image
183         c << 'RUN apt-get update'
184         c << 'RUN apt-get install -qy --no-install-recommends %s' % pkges
185         c << 'CMD /usr/lib/quagga/bgpd'
186
187         self.cmd.sudo('rm -rf %s' % workdir)
188         self.cmd.execute('mkdir -p %s' % workdir)
189         self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
190         self.build(tagname, workdir)
191         return tagname
192
193     def create_ryu(self, tagname='ryu', image=None, check_exist=False):
194         if check_exist and self.exist(tagname):
195             return tagname
196         workdir = os.path.join(TEST_BASE_DIR, tagname)
197         workdir_ctn = '/root/osrg/ryu'
198         pkges = ' '.join([
199             'tcpdump',
200             'iproute2',
201         ])
202         if image:
203             use_image = image
204         else:
205             use_image = self.baseimage
206         c = CmdBuffer()
207         c << 'FROM %s' % use_image
208         c << 'ADD ryu %s' % workdir_ctn
209         install = ' '.join([
210             'RUN apt-get update',
211             '&& apt-get install -qy --no-install-recommends %s' % pkges,
212             '&& cd %s' % workdir_ctn,
213             # Note: Clean previous builds, because "python setup.py install"
214             # might fail if the current directory contains the symlink to
215             # Docker host file systems.
216             '&& rm -rf *.egg-info/ build/ dist/ .tox/ *.log'
217             '&& pip install -r tools/pip-requires -r tools/optional-requires',
218             '&& python setup.py install',
219         ])
220         c << install
221
222         self.cmd.sudo('rm -rf %s' % workdir)
223         self.cmd.execute('mkdir -p %s' % workdir)
224         self.cmd.execute("echo '%s' > %s/Dockerfile" % (str(c), workdir))
225         self.cmd.execute('cp -r ../ryu %s/' % workdir)
226         self.build(tagname, workdir)
227         return tagname
228
229
230 class Bridge(object):
231     def __init__(self, name, subnet='', start_ip=None, end_ip=None,
232                  with_ip=True, self_ip=False,
233                  fixed_ip=None, reuse=False,
234                  br_type='docker'):
235         """Manage a bridge
236         :Parameters:
237             - name: bridge name
238             - subnet: network cider to be used in this bridge
239             - start_ip: start address of an ip to be used in the subnet
240             - end_ip: end address of an ip to be used in the subnet
241             - with_ip: specify if assign automatically an ip address
242             - self_ip: specify if assign an ip address for the bridge
243             - fixed_ip: an ip address to be assigned to the bridge
244             - reuse: specify if use an existing bridge
245             - br_type: One either in a 'docker', 'brctl' or 'ovs'
246         """
247         self.cmd = Command()
248         self.name = name
249         if br_type not in (BRIDGE_TYPE_DOCKER, BRIDGE_TYPE_BRCTL,
250                            BRIDGE_TYPE_OVS):
251             raise Exception("argument error br_type: %s" % br_type)
252         self.br_type = br_type
253         self.docker_nw = bool(self.br_type == BRIDGE_TYPE_DOCKER)
254         if TEST_PREFIX != '':
255             self.name = '{0}_{1}'.format(TEST_PREFIX, name)
256         self.with_ip = with_ip
257         if with_ip:
258             self.subnet = netaddr.IPNetwork(subnet)
259             if start_ip:
260                 self.start_ip = start_ip
261             else:
262                 self.start_ip = netaddr.IPAddress(self.subnet.first)
263             if end_ip:
264                 self.end_ip = end_ip
265             else:
266                 self.end_ip = netaddr.IPAddress(self.subnet.last)
267
268             def _ip_gen():
269                 for host in netaddr.IPRange(self.start_ip, self.end_ip):
270                     yield host
271             self._ip_generator = _ip_gen()
272             # throw away first network address
273             self.next_ip_address()
274
275         self.self_ip = self_ip
276         if fixed_ip:
277             self.ip_addr = fixed_ip
278         else:
279             self.ip_addr = self.next_ip_address()
280         if not reuse:
281             def f():
282                 if self.br_type == BRIDGE_TYPE_DOCKER:
283                     gw = "--gateway %s" % self.ip_addr.split('/')[0]
284                     v6 = ''
285                     if self.subnet.version == 6:
286                         v6 = '--ipv6'
287                     cmd = ("docker network create --driver bridge %s "
288                            "%s --subnet %s %s" % (v6, gw, subnet, self.name))
289                 elif self.br_type == BRIDGE_TYPE_BRCTL:
290                     cmd = "ip link add {0} type bridge".format(self.name)
291                 elif self.br_type == BRIDGE_TYPE_OVS:
292                     cmd = "ovs-vsctl add-br {0}".format(self.name)
293                 else:
294                     raise ValueError('Unsupported br_type: %s' % self.br_type)
295                 self.delete()
296                 self.execute(cmd, sudo=True, retry=True)
297             try_several_times(f)
298         if not self.docker_nw:
299             self.execute("ip link set up dev {0}".format(self.name),
300                          sudo=True, retry=True)
301
302         if not self.docker_nw and self_ip:
303             ips = self.check_br_addr(self.name)
304             for key, ip in ips.items():
305                 if self.subnet.version == key:
306                     self.execute(
307                         "ip addr del {0} dev {1}".format(ip, self.name),
308                         sudo=True, retry=True)
309             self.execute(
310                 "ip addr add {0} dev {1}".format(self.ip_addr, self.name),
311                 sudo=True, retry=True)
312         self.ctns = []
313
314     def get_bridges_dc(self):
315         out = self.execute('docker network ls', sudo=True, retry=True)
316         bridges = []
317         for line in out.splitlines()[1:]:
318             bridges.append(line.split()[1])
319         return bridges
320
321     def get_bridges_brctl(self):
322         out = self.execute('brctl show', retry=True)
323         bridges = []
324         for line in out.splitlines()[1:]:
325             bridges.append(line.split()[0])
326         return bridges
327
328     def get_bridges_ovs(self):
329         out = self.execute('ovs-vsctl list-br', sudo=True, retry=True)
330         return out.splitlines()
331
332     def get_bridges(self):
333         if self.br_type == BRIDGE_TYPE_DOCKER:
334             return self.get_bridges_dc()
335         elif self.br_type == BRIDGE_TYPE_BRCTL:
336             return self.get_bridges_brctl()
337         elif self.br_type == BRIDGE_TYPE_OVS:
338             return self.get_bridges_ovs()
339
340     def exist(self):
341         return self.name in self.get_bridges()
342
343     def execute(self, cmd, capture=True, sudo=False, retry=False):
344         if sudo:
345             m = self.cmd.sudo
346         else:
347             m = self.cmd.execute
348         if retry:
349             return m(cmd, capture=capture, try_times=3, interval=1)
350         else:
351             return m(cmd, capture=capture)
352
353     def check_br_addr(self, br):
354         ips = {}
355         cmd = "ip a show dev %s" % br
356         for line in self.execute(cmd, sudo=True).split('\n'):
357             if line.strip().startswith("inet "):
358                 elems = [e.strip() for e in line.strip().split(' ')]
359                 ips[4] = elems[1]
360             elif line.strip().startswith("inet6 "):
361                 elems = [e.strip() for e in line.strip().split(' ')]
362                 ips[6] = elems[1]
363         return ips
364
365     def next_ip_address(self):
366         return "{0}/{1}".format(next(self._ip_generator),
367                                 self.subnet.prefixlen)
368
369     def addif(self, ctn):
370         name = ctn.next_if_name()
371         self.ctns.append(ctn)
372         ip_address = None
373         if self.docker_nw:
374             ipv4 = None
375             ipv6 = None
376             ip_address = self.next_ip_address()
377             ip_address_ip = ip_address.split('/')[0]
378             version = 4
379             if netaddr.IPNetwork(ip_address).version == 6:
380                 version = 6
381             opt_ip = "--ip %s" % ip_address_ip
382             if version == 4:
383                 ipv4 = ip_address
384             else:
385                 opt_ip = "--ip6 %s" % ip_address_ip
386                 ipv6 = ip_address
387             cmd = "docker network connect %s %s %s" % (
388                 opt_ip, self.name, ctn.docker_name())
389             self.execute(cmd, sudo=True)
390             ctn.set_addr_info(bridge=self.name, ipv4=ipv4, ipv6=ipv6,
391                               ifname=name)
392         else:
393             if self.with_ip:
394                 ip_address = self.next_ip_address()
395                 version = 4
396                 if netaddr.IPNetwork(ip_address).version == 6:
397                     version = 6
398                 ctn.pipework(self, ip_address, name, version=version)
399             else:
400                 ctn.pipework(self, '0/0', name)
401         return ip_address
402
403     def delete(self, check_exist=True):
404         if check_exist:
405             if not self.exist():
406                 return
407         if self.br_type == BRIDGE_TYPE_DOCKER:
408             self.execute("docker network rm %s" % self.name,
409                          sudo=True, retry=True)
410         elif self.br_type == BRIDGE_TYPE_BRCTL:
411             self.execute("ip link set down dev %s" % self.name,
412                          sudo=True, retry=True)
413             self.execute(
414                 "ip link delete %s type bridge" % self.name,
415                 sudo=True, retry=True)
416         elif self.br_type == BRIDGE_TYPE_OVS:
417             self.execute(
418                 "ovs-vsctl del-br %s" % self.name,
419                 sudo=True, retry=True)
420
421
422 class Container(object):
423     def __init__(self, name, image=None):
424         self.name = name
425         self.image = image
426         self.shared_volumes = []
427         self.ip_addrs = []
428         self.ip6_addrs = []
429         self.is_running = False
430         self.eths = []
431         self.id = None
432
433         self.cmd = Command()
434         self.remove()
435
436     def docker_name(self):
437         if TEST_PREFIX == DEFAULT_TEST_PREFIX:
438             return self.name
439         return '{0}_{1}'.format(TEST_PREFIX, self.name)
440
441     def get_docker_id(self):
442         if self.id:
443             return self.id
444         else:
445             return self.docker_name()
446
447     def next_if_name(self):
448         name = 'eth{0}'.format(len(self.eths) + 1)
449         self.eths.append(name)
450         return name
451
452     def set_addr_info(self, bridge, ipv4=None, ipv6=None, ifname='eth0'):
453         if ipv4:
454             self.ip_addrs.append((ifname, ipv4, bridge))
455         if ipv6:
456             self.ip6_addrs.append((ifname, ipv6, bridge))
457
458     def get_addr_info(self, bridge, ipv=4):
459         addrinfo = {}
460         if ipv == 4:
461             ip_addrs = self.ip_addrs
462         elif ipv == 6:
463             ip_addrs = self.ip6_addrs
464         else:
465             return None
466         for addr in ip_addrs:
467             if addr[2] == bridge:
468                 addrinfo[addr[1]] = addr[0]
469         return addrinfo
470
471     def execute(self, cmd, capture=True, sudo=False, retry=False):
472         if sudo:
473             m = self.cmd.sudo
474         else:
475             m = self.cmd.execute
476         if retry:
477             return m(cmd, capture=capture, try_times=3, interval=1)
478         else:
479             return m(cmd, capture=capture)
480
481     def dcexec(self, cmd, capture=True, retry=False):
482         if retry:
483             return self.cmd.sudo(cmd, capture=capture, try_times=3, interval=1)
484         else:
485             return self.cmd.sudo(cmd, capture=capture)
486
487     def exec_on_ctn(self, cmd, capture=True, detach=False):
488         name = self.docker_name()
489         flag = '-d' if detach else ''
490         return self.dcexec('docker exec {0} {1} {2}'.format(
491             flag, name, cmd), capture=capture)
492
493     def get_containers(self, allctn=False):
494         cmd = 'docker ps --no-trunc=true'
495         if allctn:
496             cmd += ' --all=true'
497         out = self.dcexec(cmd, retry=True)
498         containers = []
499         for line in out.splitlines()[1:]:
500             containers.append(line.split()[-1])
501         return containers
502
503     def exist(self, allctn=False):
504         return self.docker_name() in self.get_containers(allctn=allctn)
505
506     def run(self):
507         c = CmdBuffer(' ')
508         c << "docker run --privileged=true"
509         for sv in self.shared_volumes:
510             c << "-v {0}:{1}".format(sv[0], sv[1])
511         c << "--name {0} --hostname {0} -id {1}".format(self.docker_name(),
512                                                         self.image)
513         self.id = self.dcexec(str(c), retry=True)
514         self.is_running = True
515         self.exec_on_ctn("ip li set up dev lo")
516         ipv4 = None
517         ipv6 = None
518         for line in self.exec_on_ctn("ip a show dev eth0").split('\n'):
519             if line.strip().startswith("inet "):
520                 elems = [e.strip() for e in line.strip().split(' ')]
521                 ipv4 = elems[1]
522             elif line.strip().startswith("inet6 "):
523                 elems = [e.strip() for e in line.strip().split(' ')]
524                 ipv6 = elems[1]
525         self.set_addr_info(bridge='docker0', ipv4=ipv4, ipv6=ipv6,
526                            ifname='eth0')
527         return 0
528
529     def stop(self, check_exist=True):
530         if check_exist:
531             if not self.exist(allctn=False):
532                 return
533         ctn_id = self.get_docker_id()
534         out = self.dcexec('docker stop -t 0 %s' % ctn_id, retry=True)
535         self.is_running = False
536         return out
537
538     def remove(self, check_exist=True):
539         if check_exist:
540             if not self.exist(allctn=True):
541                 return
542         ctn_id = self.get_docker_id()
543         out = self.dcexec('docker rm -f %s' % ctn_id, retry=True)
544         self.is_running = False
545         return out
546
547     def pipework(self, bridge, ip_addr, intf_name="", version=4):
548         if not self.is_running:
549             LOG.warning('Call run() before pipeworking')
550             return
551         c = CmdBuffer(' ')
552         c << "pipework {0}".format(bridge.name)
553
554         if intf_name != "":
555             c << "-i {0}".format(intf_name)
556         else:
557             intf_name = "eth1"
558         ipv4 = None
559         ipv6 = None
560         if version == 4:
561             ipv4 = ip_addr
562         else:
563             c << '-a 6'
564             ipv6 = ip_addr
565         c << "{0} {1}".format(self.docker_name(), ip_addr)
566         self.set_addr_info(bridge=bridge.name, ipv4=ipv4, ipv6=ipv6,
567                            ifname=intf_name)
568         self.execute(str(c), sudo=True, retry=True)
569
570     def get_pid(self):
571         if self.is_running:
572             cmd = "docker inspect -f '{{.State.Pid}}' %s" % self.docker_name()
573             return int(self.dcexec(cmd))
574         return -1
575
576     def start_tcpdump(self, interface=None, filename=None):
577         if not interface:
578             interface = "eth0"
579         if not filename:
580             filename = "{0}/{1}.dump".format(
581                 self.shared_volumes[0][1], interface)
582         self.exec_on_ctn(
583             "tcpdump -i {0} -w {1}".format(interface, filename),
584             detach=True)
585
586
587 class BGPContainer(Container):
588
589     WAIT_FOR_BOOT = 1
590     RETRY_INTERVAL = 5
591     DEFAULT_PEER_ARG = {'neigh_addr': '',
592                         'passwd': None,
593                         'vpn': False,
594                         'flowspec': False,
595                         'is_rs_client': False,
596                         'is_rr_client': False,
597                         'cluster_id': None,
598                         'policies': None,
599                         'passive': False,
600                         'local_addr': '',
601                         'as2': False,
602                         'graceful_restart': None,
603                         'local_as': None,
604                         'prefix_limit': None}
605     default_peer_keys = sorted(DEFAULT_PEER_ARG.keys())
606     DEFAULT_ROUTE_ARG = {'prefix': None,
607                          'rf': 'ipv4',
608                          'attr': None,
609                          'next-hop': None,
610                          'as-path': None,
611                          'community': None,
612                          'med': None,
613                          'local-pref': None,
614                          'extended-community': None,
615                          'matchs': None,
616                          'thens': None}
617     default_route_keys = sorted(DEFAULT_ROUTE_ARG.keys())
618
619     def __init__(self, name, asn, router_id, ctn_image_name=None):
620         self.config_dir = TEST_BASE_DIR
621         if TEST_PREFIX:
622             self.config_dir = os.path.join(self.config_dir, TEST_PREFIX)
623         self.config_dir = os.path.join(self.config_dir, name)
624         self.asn = asn
625         self.router_id = router_id
626         self.peers = {}
627         self.routes = {}
628         self.policies = {}
629         super(BGPContainer, self).__init__(name, ctn_image_name)
630         self.execute(
631             'rm -rf {0}'.format(self.config_dir), sudo=True)
632         self.execute('mkdir -p {0}'.format(self.config_dir))
633         self.execute('chmod 777 {0}'.format(self.config_dir))
634
635     def __repr__(self):
636         return str({'name': self.name, 'asn': self.asn,
637                     'router_id': self.router_id})
638
639     def run(self, wait=False, w_time=WAIT_FOR_BOOT):
640         self.create_config()
641         super(BGPContainer, self).run()
642         if wait:
643             time.sleep(w_time)
644         return w_time
645
646     def add_peer(self, peer, bridge='', reload_config=True, v6=False,
647                  peer_info=None):
648         peer_info = peer_info or {}
649         self.peers[peer] = self.DEFAULT_PEER_ARG.copy()
650         self.peers[peer].update(peer_info)
651         peer_keys = sorted(self.peers[peer].keys())
652         if peer_keys != self.default_peer_keys:
653             raise Exception("argument error peer_info: %s" % peer_info)
654
655         neigh_addr = ''
656         local_addr = ''
657         it = itertools.product(self.ip_addrs, peer.ip_addrs)
658         if v6:
659             it = itertools.product(self.ip6_addrs, peer.ip6_addrs)
660
661         for me, you in it:
662             if bridge != '' and bridge != me[2]:
663                 continue
664             if me[2] == you[2]:
665                 neigh_addr = you[1]
666                 local_addr = me[1]
667                 if v6:
668                     addr, mask = local_addr.split('/')
669                     local_addr = "{0}%{1}/{2}".format(addr, me[0], mask)
670                 break
671
672         if neigh_addr == '':
673             raise Exception('peer {0} seems not ip reachable'.format(peer))
674
675         if not self.peers[peer]['policies']:
676             self.peers[peer]['policies'] = {}
677
678         self.peers[peer]['neigh_addr'] = neigh_addr
679         self.peers[peer]['local_addr'] = local_addr
680         if self.is_running and reload_config:
681             self.create_config()
682             self.reload_config()
683
684     def del_peer(self, peer, reload_config=True):
685         del self.peers[peer]
686         if self.is_running and reload_config:
687             self.create_config()
688             self.reload_config()
689
690     def disable_peer(self, peer):
691         raise NotImplementedError()
692
693     def enable_peer(self, peer):
694         raise NotImplementedError()
695
696     def log(self):
697         return self.execute('cat {0}/*.log'.format(self.config_dir))
698
699     def add_route(self, route, reload_config=True, route_info=None):
700         route_info = route_info or {}
701         self.routes[route] = self.DEFAULT_ROUTE_ARG.copy()
702         self.routes[route].update(route_info)
703         route_keys = sorted(self.routes[route].keys())
704         if route_keys != self.default_route_keys:
705             raise Exception("argument error route_info: %s" % route_info)
706         self.routes[route]['prefix'] = route
707         if self.is_running and reload_config:
708             self.create_config()
709             self.reload_config()
710
711     def add_policy(self, policy, peer, typ, default='accept',
712                    reload_config=True):
713         self.set_default_policy(peer, typ, default)
714         self.define_policy(policy)
715         self.assign_policy(peer, policy, typ)
716         if self.is_running and reload_config:
717             self.create_config()
718             self.reload_config()
719
720     def set_default_policy(self, peer, typ, default):
721         if (typ in ['in', 'out', 'import', 'export'] and
722                 default in ['reject', 'accept']):
723             if 'default-policy' not in self.peers[peer]:
724                 self.peers[peer]['default-policy'] = {}
725             self.peers[peer]['default-policy'][typ] = default
726         else:
727             raise Exception('wrong type or default')
728
729     def define_policy(self, policy):
730         self.policies[policy['name']] = policy
731
732     def assign_policy(self, peer, policy, typ):
733         if peer not in self.peers:
734             raise Exception('peer {0} not found'.format(peer.name))
735         name = policy['name']
736         if name not in self.policies:
737             raise Exception('policy {0} not found'.format(name))
738         self.peers[peer]['policies'][typ] = policy
739
740     def get_local_rib(self, peer, rf):
741         raise NotImplementedError()
742
743     def get_global_rib(self, rf):
744         raise NotImplementedError()
745
746     def get_neighbor_state(self, peer_id):
747         raise NotImplementedError()
748
749     def get_reachablily(self, prefix, timeout=20):
750         version = netaddr.IPNetwork(prefix).version
751         addr = prefix.split('/')[0]
752         if version == 4:
753             ping_cmd = 'ping'
754         elif version == 6:
755             ping_cmd = 'ping6'
756         else:
757             raise Exception(
758                 'unsupported route family: {0}'.format(version))
759         cmd = '/bin/bash -c "/bin/{0} -c 1 -w 1 {1} | xargs echo"'.format(
760             ping_cmd, addr)
761         interval = 1
762         count = 0
763         while True:
764             res = self.exec_on_ctn(cmd)
765             LOG.info(res)
766             if '1 packets received' in res and '0% packet loss':
767                 break
768             time.sleep(interval)
769             count += interval
770             if count >= timeout:
771                 raise Exception('timeout')
772         return True
773
774     def wait_for(self, expected_state, peer, timeout=120):
775         interval = 1
776         count = 0
777         while True:
778             state = self.get_neighbor_state(peer)
779             LOG.info("%s's peer %s state: %s",
780                      self.router_id, peer.router_id, state)
781             if state == expected_state:
782                 return
783
784             time.sleep(interval)
785             count += interval
786             if count >= timeout:
787                 raise Exception('timeout')
788
789     def add_static_route(self, network, next_hop):
790         cmd = '/sbin/ip route add {0} via {1}'.format(network, next_hop)
791         self.exec_on_ctn(cmd)
792
793     def set_ipv6_forward(self):
794         cmd = 'sysctl -w net.ipv6.conf.all.forwarding=1'
795         self.exec_on_ctn(cmd)
796
797     def create_config(self):
798         raise NotImplementedError()
799
800     def reload_config(self):
801         raise NotImplementedError()