]> git.proxmox.com Git - mirror_frr.git/blob - tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py
(all tests): Add extra check to make sure daemons are still running after each essent...
[mirror_frr.git] / tests / topotests / bgp_multiview_topo1 / test_bgp_multiview_topo1.py
1 #!/usr/bin/env python
2
3 #
4 # test_bgp_multiview_topo1.py
5 # Part of NetDEF Topology Tests
6 #
7 # Copyright (c) 2016 by
8 # Network Device Education Foundation, Inc. ("NetDEF")
9 #
10 # Permission to use, copy, modify, and/or distribute this software
11 # for any purpose with or without fee is hereby granted, provided
12 # that the above copyright notice and this permission notice appear
13 # in all copies.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
16 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
18 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
19 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
21 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22 # OF THIS SOFTWARE.
23 #
24
25 """
26 test_bgp_multiview_topo1.py: Simple Quagga/FRR Route-Server Test
27
28 +----------+ +----------+ +----------+ +----------+ +----------+
29 | peer1 | | peer2 | | peer3 | | peer4 | | peer5 |
30 | AS 65001 | | AS 65002 | | AS 65003 | | AS 65004 | | AS 65005 |
31 +-----+----+ +-----+----+ +-----+----+ +-----+----+ +-----+----+
32 | .1 | .2 | .3 | .4 | .5
33 | ______/ / / _________/
34 \ / ________________/ / /
35 | | / _________________________/ / +----------+
36 | | | / __________________________/ ___| peer6 |
37 | | | | / ____________________________/.6 | AS 65006 |
38 | | | | | / _________________________ +----------+
39 | | | | | | / __________________ \ +----------+
40 | | | | | | | / \ \___| peer7 |
41 | | | | | | | | \ .7 | AS 65007 |
42 ~~~~~~~~~~~~~~~~~~~~~ \ +----------+
43 ~~ SW1 ~~ \ +----------+
44 ~~ Switch ~~ \_____| peer8 |
45 ~~ 172.16.1.0/24 ~~ .8 | AS 65008 |
46 ~~~~~~~~~~~~~~~~~~~~~ +----------+
47 |
48 | .254
49 +---------+---------+
50 | FRR R1 |
51 | BGP Multi-View |
52 | Peer 1-3 > View 1 |
53 | Peer 4-5 > View 2 |
54 | Peer 6-8 > View 3 |
55 +---------+---------+
56 | .1
57 |
58 ~~~~~~~~~~~~~ Stub Network is redistributed
59 ~~ SW0 ~~ into each BGP view with different
60 ~~ 172.20.0.1/28 ~~ attributes (using route-map)
61 ~~ Stub Switch ~~
62 ~~~~~~~~~~~~~
63 """
64
65 import os
66 import re
67 import sys
68 import difflib
69 import pytest
70 from time import sleep
71
72 from mininet.topo import Topo
73 from mininet.net import Mininet
74 from mininet.node import Node, OVSSwitch, Host
75 from mininet.log import setLogLevel, info
76 from mininet.cli import CLI
77 from mininet.link import Intf
78
79 from functools import partial
80
81 sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
82 from lib import topotest
83
84 fatal_error = ""
85
86
87 #####################################################
88 ##
89 ## Network Topology Definition
90 ##
91 #####################################################
92
93 class NetworkTopo(Topo):
94 "BGP Multiview Topology 1"
95
96 def build(self, **_opts):
97
98 exabgpPrivateDirs = ['/etc/exabgp',
99 '/var/run/exabgp',
100 '/var/log']
101
102 # Setup Routers
103 router = {}
104 for i in range(1, 2):
105 router[i] = topotest.addRouter(self, 'r%s' % i)
106
107 # Setup Provider BGP peers
108 peer = {}
109 for i in range(1, 9):
110 peer[i] = self.addHost('peer%s' % i, ip='172.16.1.%s/24' % i,
111 defaultRoute='via 172.16.1.254',
112 privateDirs=exabgpPrivateDirs)
113
114 # Setup Switches
115 switch = {}
116 # First switch is for a dummy interface (for local network)
117 switch[0] = self.addSwitch('sw0', cls=topotest.LegacySwitch)
118 self.addLink(switch[0], router[1], intfName2='r1-stub')
119 # Second switch is for connection to all peering routers
120 switch[1] = self.addSwitch('sw1', cls=topotest.LegacySwitch)
121 self.addLink(switch[1], router[1], intfName2='r1-eth0')
122 for j in range(1, 9):
123 self.addLink(switch[1], peer[j], intfName2='peer%s-eth0' % j)
124
125
126 #####################################################
127 ##
128 ## Tests starting
129 ##
130 #####################################################
131
132 def setup_module(module):
133 global topo, net
134
135 print("\n\n** %s: Setup Topology" % module.__name__)
136 print("******************************************\n")
137
138 print("Cleanup old Mininet runs")
139 os.system('sudo mn -c > /dev/null 2>&1')
140
141 thisDir = os.path.dirname(os.path.realpath(__file__))
142 topo = NetworkTopo()
143
144 net = Mininet(controller=None, topo=topo)
145 net.start()
146
147 # Starting Routers
148 for i in range(1, 2):
149 net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i))
150 net['r%s' % i].loadConf('bgpd', '%s/r%s/bgpd.conf' % (thisDir, i))
151 net['r%s' % i].startRouter()
152
153 # Starting PE Hosts and init ExaBGP on each of them
154 print('*** Starting BGP on all 8 Peers in 10s')
155 sleep(10)
156 for i in range(1, 9):
157 net['peer%s' % i].cmd('cp %s/exabgp.env /etc/exabgp/exabgp.env' % thisDir)
158 net['peer%s' % i].cmd('cp %s/peer%s/* /etc/exabgp/' % (thisDir, i))
159 net['peer%s' % i].cmd('chmod 644 /etc/exabgp/*')
160 net['peer%s' % i].cmd('chmod 755 /etc/exabgp/*.py')
161 net['peer%s' % i].cmd('chown -R exabgp:exabgp /etc/exabgp')
162 net['peer%s' % i].cmd('exabgp -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg')
163 print('peer%s' % i),
164 print('')
165
166 # For debugging after starting Quagga/FRR daemons, uncomment the next line
167 # CLI(net)
168
169 def teardown_module(module):
170 global net
171
172 print("\n\n** %s: Shutdown Topology" % module.__name__)
173 print("******************************************\n")
174
175 # Shutdown - clean up everything
176 print('*** Killing BGP on Peer routers')
177 # Killing ExaBGP
178 for i in range(1, 9):
179 net['peer%s' % i].cmd('kill `cat /var/run/exabgp/exabgp.pid`')
180
181 # End - Shutdown network
182 net.stop()
183
184 def test_router_running():
185 global fatal_error
186 global net
187
188 # Skip if previous fatal error condition is raised
189 if (fatal_error != ""):
190 pytest.skip(fatal_error)
191
192 print("\n\n** Check if FRR/Quagga is running on each Router node")
193 print("******************************************\n")
194 sleep(5)
195
196 # Starting Routers
197 for i in range(1, 2):
198 fatal_error = net['r%s' % i].checkRouterRunning()
199 assert fatal_error == "", fatal_error
200
201 # For debugging after starting FRR/Quagga daemons, uncomment the next line
202 # CLI(net)
203
204
205 def test_bgp_converge():
206 "Check for BGP converged on all peers and BGP views"
207
208 global fatal_error
209 global net
210
211 # Skip if previous fatal error condition is raised
212 if (fatal_error != ""):
213 pytest.skip(fatal_error)
214
215 # Wait for BGP to converge (All Neighbors in either Full or TwoWay State)
216 print("\n\n** Verify for BGP to converge")
217 print("******************************************\n")
218 timeout = 60
219 while timeout > 0:
220 print("Timeout in %s: " % timeout),
221 sys.stdout.flush()
222 # Look for any node not yet converged
223 for i in range(1, 2):
224 for view in range(1, 4):
225 notConverged = net['r%s' % i].cmd('vtysh -c "show ip bgp view %s summary" 2> /dev/null | grep ^[0-9] | grep -v " 11$"' % view)
226 if notConverged:
227 print('Waiting for r%s, view %s' % (i, view))
228 sys.stdout.flush()
229 break
230 if notConverged:
231 break
232 if notConverged:
233 sleep(5)
234 timeout -= 5
235 else:
236 print('Done')
237 break
238 else:
239 # Bail out with error if a router fails to converge
240 bgpStatus = net['r%s' % i].cmd('vtysh -c "show ip bgp view %s summary"' % view)
241 assert False, "BGP did not converge:\n%s" % bgpStatus
242
243 # Wait for an extra 30s to announce all routes
244 print('Waiting 30s for routes to be announced');
245 sleep(30)
246
247 print("BGP converged.")
248
249 # if timeout < 60:
250 # # Only wait if we actually went through a convergence
251 # print("\nwaiting 15s for routes to populate")
252 # sleep(15)
253
254 # Make sure that all daemons are running
255 for i in range(1, 2):
256 fatal_error = net['r%s' % i].checkRouterRunning()
257 assert fatal_error == "", fatal_error
258
259 # For debugging after starting Quagga/FRR daemons, uncomment the next line
260 # CLI(net)
261
262 def test_bgp_routingTable():
263 global fatal_error
264 global net
265
266 # Skip if previous fatal error condition is raised
267 if (fatal_error != ""):
268 pytest.skip(fatal_error)
269
270 thisDir = os.path.dirname(os.path.realpath(__file__))
271
272 print("\n\n** Verifying BGP Routing Tables")
273 print("******************************************\n")
274 failures = 0
275 for i in range(1, 2):
276 for view in range(1, 4):
277 refTableFile = '%s/r%s/show_ip_bgp_view_%s.ref' % (thisDir, i, view)
278 if os.path.isfile(refTableFile):
279 # Read expected result from file
280 expected = open(refTableFile).read().rstrip()
281 # Fix newlines (make them all the same)
282 expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
283
284 # Actual output from router
285 actual = net['r%s' % i].cmd('vtysh -c "show ip bgp view %s" 2> /dev/null' % view).rstrip()
286
287 # Fix inconsitent spaces between 0.99.24 and newer versions of Quagga...
288 actual = re.sub('0 0', '0 0', actual)
289 actual = re.sub(r'([0-9]) 32768', r'\1 32768', actual)
290 # Remove summary line (changed recently)
291 actual = re.sub(r'Total number.*', '', actual)
292 actual = re.sub(r'Displayed.*', '', actual)
293 actual = actual.rstrip()
294 # Fix table version (ignore it)
295 actual = re.sub(r'(BGP table version is )[0-9]+', r'\1XXX', actual)
296
297 # Fix newlines (make them all the same)
298 actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
299
300 # Generate Diff
301 diff = ''.join(difflib.context_diff(actual, expected,
302 fromfile="actual BGP routing table",
303 tofile="expected BGP routing table"))
304 # Empty string if it matches, otherwise diff contains unified diff
305
306 if diff:
307 sys.stderr.write('r%s failed Routing Table Check for view %s:\n%s\n'
308 % (i, view, diff))
309 failures += 1
310 else:
311 print("r%s ok" % i)
312
313 assert failures == 0, "Routing Table verification failed for router r%s, view %s:\n%s" % (i, view, diff)
314
315 # Make sure that all daemons are running
316 for i in range(1, 2):
317 fatal_error = net['r%s' % i].checkRouterRunning()
318 assert fatal_error == "", fatal_error
319
320 # For debugging after starting FRR/Quagga daemons, uncomment the next line
321 # CLI(net)
322
323
324 def test_shutdown_check_stderr():
325 global fatal_error
326 global net
327
328 # Skip if previous fatal error condition is raised
329 if (fatal_error != ""):
330 pytest.skip(fatal_error)
331
332 if os.environ.get('TOPOTESTS_CHECK_STDERR') is None:
333 print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n")
334 pytest.skip('Skipping test for Stderr output')
335
336 thisDir = os.path.dirname(os.path.realpath(__file__))
337
338 print("\n\n** Verifying unexpected STDERR output from daemons")
339 print("******************************************\n")
340
341 net['r1'].stopRouter()
342
343 log = net['r1'].getStdErr('bgpd')
344 print("\nBGPd StdErr Log:\n" + log)
345 log = net['r1'].getStdErr('zebra')
346 print("\nZebra StdErr Log:\n" + log)
347
348
349 def test_shutdown_check_memleak():
350 global fatal_error
351 global net
352
353 # Skip if previous fatal error condition is raised
354 if (fatal_error != ""):
355 pytest.skip(fatal_error)
356
357 if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None:
358 print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n")
359 pytest.skip('Skipping test for memory leaks')
360
361 thisDir = os.path.dirname(os.path.realpath(__file__))
362
363 net['r1'].stopRouter()
364 net['r1'].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__))
365
366
367 if __name__ == '__main__':
368
369 setLogLevel('info')
370 # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
371 # retval = pytest.main(["-s", "--tb=no"])
372 retval = pytest.main(["-s"])
373 sys.exit(retval)