backing up
[vsorcdistro/.git] / ryu / build / lib.linux-armv7l-2.7 / ryu / tests / integrated / common / quagga.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/quagga.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 logging
22 import os
23
24 import netaddr
25
26 from . import docker_base as base
27
28 LOG = logging.getLogger(__name__)
29
30
31 class QuaggaBGPContainer(base.BGPContainer):
32
33     WAIT_FOR_BOOT = 1
34     SHARED_VOLUME = '/etc/quagga'
35
36     def __init__(self, name, asn, router_id, ctn_image_name, zebra=False):
37         super(QuaggaBGPContainer, self).__init__(name, asn, router_id,
38                                                  ctn_image_name)
39         self.shared_volumes.append((self.config_dir, self.SHARED_VOLUME))
40         self.zebra = zebra
41         self._create_config_debian()
42
43     def run(self, wait=False, w_time=WAIT_FOR_BOOT):
44         w_time = super(QuaggaBGPContainer,
45                        self).run(wait=wait, w_time=self.WAIT_FOR_BOOT)
46         return w_time
47
48     def get_global_rib(self, prefix='', rf='ipv4'):
49         rib = []
50         if prefix != '':
51             return self.get_global_rib_with_prefix(prefix, rf)
52
53         out = self.vtysh('show bgp {0} unicast'.format(rf), config=False)
54         if out.startswith('No BGP network exists'):
55             return rib
56
57         read_next = False
58
59         for line in out.split('\n'):
60             ibgp = False
61             if line[:2] == '*>':
62                 line = line[2:]
63                 if line[0] == 'i':
64                     line = line[1:]
65                     ibgp = True
66             elif not read_next:
67                 continue
68
69             elems = line.split()
70
71             if len(elems) == 1:
72                 read_next = True
73                 prefix = elems[0]
74                 continue
75             elif read_next:
76                 nexthop = elems[0]
77             else:
78                 prefix = elems[0]
79                 nexthop = elems[1]
80             read_next = False
81
82             rib.append({'prefix': prefix, 'nexthop': nexthop,
83                         'ibgp': ibgp})
84
85         return rib
86
87     def get_global_rib_with_prefix(self, prefix, rf):
88         rib = []
89
90         lines = [line.strip() for line in self.vtysh(
91             'show bgp {0} unicast {1}'.format(rf, prefix),
92             config=False).split('\n')]
93
94         if lines[0] == '% Network not in table':
95             return rib
96
97         lines = lines[2:]
98
99         if lines[0].startswith('Not advertised'):
100             lines.pop(0)  # another useless line
101         elif lines[0].startswith('Advertised to non peer-group peers:'):
102             lines = lines[2:]  # other useless lines
103         else:
104             raise Exception('unknown output format {0}'.format(lines))
105
106         if lines[0] == 'Local':
107             aspath = []
108         else:
109             aspath = [int(asn) for asn in lines[0].split()]
110
111         nexthop = lines[1].split()[0].strip()
112         info = [s.strip(',') for s in lines[2].split()]
113         attrs = []
114         if 'metric' in info:
115             med = info[info.index('metric') + 1]
116             attrs.append({'type': base.BGP_ATTR_TYPE_MULTI_EXIT_DISC,
117                           'metric': int(med)})
118         if 'localpref' in info:
119             localpref = info[info.index('localpref') + 1]
120             attrs.append({'type': base.BGP_ATTR_TYPE_LOCAL_PREF,
121                           'value': int(localpref)})
122
123         rib.append({'prefix': prefix, 'nexthop': nexthop,
124                     'aspath': aspath, 'attrs': attrs})
125
126         return rib
127
128     def get_neighbor_state(self, peer):
129         if peer not in self.peers:
130             raise Exception('not found peer {0}'.format(peer.router_id))
131
132         neigh_addr = self.peers[peer]['neigh_addr'].split('/')[0]
133
134         info = [l.strip() for l in self.vtysh(
135             'show bgp neighbors {0}'.format(neigh_addr),
136             config=False).split('\n')]
137
138         if not info[0].startswith('BGP neighbor is'):
139             raise Exception('unknown format')
140
141         idx1 = info[0].index('BGP neighbor is ')
142         idx2 = info[0].index(',')
143         n_addr = info[0][idx1 + len('BGP neighbor is '):idx2]
144         if n_addr == neigh_addr:
145             idx1 = info[2].index('= ')
146             state = info[2][idx1 + len('= '):]
147             if state.startswith('Idle'):
148                 return base.BGP_FSM_IDLE
149             elif state.startswith('Active'):
150                 return base.BGP_FSM_ACTIVE
151             elif state.startswith('Established'):
152                 return base.BGP_FSM_ESTABLISHED
153             else:
154                 return state
155
156         raise Exception('not found peer {0}'.format(peer.router_id))
157
158     def send_route_refresh(self):
159         self.vtysh('clear ip bgp * soft', config=False)
160
161     def create_config(self):
162         zebra = 'no'
163         self._create_config_bgp()
164         if self.zebra:
165             zebra = 'yes'
166             self._create_config_zebra()
167         self._create_config_daemons(zebra)
168
169     def _create_config_debian(self):
170         c = base.CmdBuffer()
171         c << 'vtysh_enable=yes'
172         c << 'zebra_options="  --daemon -A 127.0.0.1"'
173         c << 'bgpd_options="   --daemon -A 127.0.0.1"'
174         c << 'ospfd_options="  --daemon -A 127.0.0.1"'
175         c << 'ospf6d_options=" --daemon -A ::1"'
176         c << 'ripd_options="   --daemon -A 127.0.0.1"'
177         c << 'ripngd_options=" --daemon -A ::1"'
178         c << 'isisd_options="  --daemon -A 127.0.0.1"'
179         c << 'babeld_options=" --daemon -A 127.0.0.1"'
180         c << 'watchquagga_enable=yes'
181         c << 'watchquagga_options=(--daemon)'
182         with open('{0}/debian.conf'.format(self.config_dir), 'w') as f:
183             LOG.info("[%s's new config]", self.name)
184             LOG.info(str(c))
185             f.writelines(str(c))
186
187     def _create_config_daemons(self, zebra='no'):
188         c = base.CmdBuffer()
189         c << 'zebra=%s' % zebra
190         c << 'bgpd=yes'
191         c << 'ospfd=no'
192         c << 'ospf6d=no'
193         c << 'ripd=no'
194         c << 'ripngd=no'
195         c << 'isisd=no'
196         c << 'babeld=no'
197         with open('{0}/daemons'.format(self.config_dir), 'w') as f:
198             LOG.info("[%s's new config]", self.name)
199             LOG.info(str(c))
200             f.writelines(str(c))
201
202     def _create_config_bgp(self):
203
204         c = base.CmdBuffer()
205         c << 'hostname bgpd'
206         c << 'password zebra'
207         c << 'router bgp {0}'.format(self.asn)
208         c << 'bgp router-id {0}'.format(self.router_id)
209         if any(info['graceful_restart'] for info in self.peers.values()):
210             c << 'bgp graceful-restart'
211
212         version = 4
213         for peer, info in self.peers.items():
214             version = netaddr.IPNetwork(info['neigh_addr']).version
215             n_addr = info['neigh_addr'].split('/')[0]
216             if version == 6:
217                 c << 'no bgp default ipv4-unicast'
218
219             c << 'neighbor {0} remote-as {1}'.format(n_addr, peer.asn)
220             if info['is_rs_client']:
221                 c << 'neighbor {0} route-server-client'.format(n_addr)
222             for typ, p in info['policies'].items():
223                 c << 'neighbor {0} route-map {1} {2}'.format(n_addr, p['name'],
224                                                              typ)
225             if info['passwd']:
226                 c << 'neighbor {0} password {1}'.format(n_addr, info['passwd'])
227             if info['passive']:
228                 c << 'neighbor {0} passive'.format(n_addr)
229             if version == 6:
230                 c << 'address-family ipv6 unicast'
231                 c << 'neighbor {0} activate'.format(n_addr)
232                 c << 'exit-address-family'
233
234         for route in self.routes.values():
235             if route['rf'] == 'ipv4':
236                 c << 'network {0}'.format(route['prefix'])
237             elif route['rf'] == 'ipv6':
238                 c << 'address-family ipv6 unicast'
239                 c << 'network {0}'.format(route['prefix'])
240                 c << 'exit-address-family'
241             else:
242                 raise Exception(
243                     'unsupported route faily: {0}'.format(route['rf']))
244
245         if self.zebra:
246             if version == 6:
247                 c << 'address-family ipv6 unicast'
248                 c << 'redistribute connected'
249                 c << 'exit-address-family'
250             else:
251                 c << 'redistribute connected'
252
253         for name, policy in self.policies.items():
254             c << 'access-list {0} {1} {2}'.format(name, policy['type'],
255                                                   policy['match'])
256             c << 'route-map {0} permit 10'.format(name)
257             c << 'match ip address {0}'.format(name)
258             c << 'set metric {0}'.format(policy['med'])
259
260         c << 'debug bgp as4'
261         c << 'debug bgp fsm'
262         c << 'debug bgp updates'
263         c << 'debug bgp events'
264         c << 'log file {0}/bgpd.log'.format(self.SHARED_VOLUME)
265
266         with open('{0}/bgpd.conf'.format(self.config_dir), 'w') as f:
267             LOG.info("[%s's new config]", self.name)
268             LOG.info(str(c))
269             f.writelines(str(c))
270
271     def _create_config_zebra(self):
272         c = base.CmdBuffer()
273         c << 'hostname zebra'
274         c << 'password zebra'
275         c << 'log file {0}/zebra.log'.format(self.SHARED_VOLUME)
276         c << 'debug zebra packet'
277         c << 'debug zebra kernel'
278         c << 'debug zebra rib'
279         c << ''
280
281         with open('{0}/zebra.conf'.format(self.config_dir), 'w') as f:
282             LOG.info("[%s's new config]", self.name)
283             LOG.info(str(c))
284             f.writelines(str(c))
285
286     def vtysh(self, cmd, config=True):
287         if not isinstance(cmd, list):
288             cmd = [cmd]
289         cmd = ' '.join("-c '{0}'".format(c) for c in cmd)
290         if config:
291             return self.exec_on_ctn(
292                 "vtysh -d bgpd -c 'en' -c 'conf t' -c "
293                 "'router bgp {0}' {1}".format(self.asn, cmd),
294                 capture=True)
295         else:
296             return self.exec_on_ctn("vtysh -d bgpd {0}".format(cmd),
297                                     capture=True)
298
299     def reload_config(self):
300         daemon = []
301         daemon.append('bgpd')
302         if self.zebra:
303             daemon.append('zebra')
304         for d in daemon:
305             cmd = '/usr/bin/pkill {0} -SIGHUP'.format(d)
306             self.exec_on_ctn(cmd, capture=True)
307
308
309 class RawQuaggaBGPContainer(QuaggaBGPContainer):
310     def __init__(self, name, config, ctn_image_name,
311                  zebra=False):
312         asn = None
313         router_id = None
314         for line in config.split('\n'):
315             line = line.strip()
316             if line.startswith('router bgp'):
317                 asn = int(line[len('router bgp'):].strip())
318             if line.startswith('bgp router-id'):
319                 router_id = line[len('bgp router-id'):].strip()
320         if not asn:
321             raise Exception('asn not in quagga config')
322         if not router_id:
323             raise Exception('router-id not in quagga config')
324         self.config = config
325         super(RawQuaggaBGPContainer, self).__init__(name, asn, router_id,
326                                                     ctn_image_name, zebra)
327
328     def create_config(self):
329         with open(os.path.join(self.config_dir, 'bgpd.conf'), 'w') as f:
330             LOG.info("[%s's new config]", self.name)
331             LOG.info(self.config)
332             f.writelines(self.config)