]> git.proxmox.com Git - mirror_frr.git/blame - doc/developer/topotests.rst
Merge pull request #13430 from opensourcerouting/feature/rip_allow-ecmp_limit
[mirror_frr.git] / doc / developer / topotests.rst
CommitLineData
370c8e07
QY
1.. _topotests:
2
3Topotests
4=========
5
77f3acb4 6Topotests is a suite of topology tests for FRR built on top of micronet.
370c8e07
QY
7
8Installation and Setup
9----------------------
10
fd23ce2c
MS
11Topotests run under python3. Additionally, for ExaBGP (which is used
12in some of the BGP tests) an older python2 version (and the python2
13version of ``pip``) must be installed.
77f3acb4 14
fd23ce2c 15Tested with Ubuntu 20.04,Ubuntu 18.04, and Debian 11.
370c8e07 16
fd23ce2c
MS
17Instructions are the same for all setups (i.e. ExaBGP is only used for
18BGP tests).
370c8e07 19
77f3acb4
CH
20Installing Topotest Requirements
21^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
370c8e07
QY
22
23.. code:: shell
24
fd23ce2c 25 apt-get install gdb
77f3acb4
CH
26 apt-get install iproute2
27 apt-get install net-tools
28 apt-get install python3-pip
29 python3 -m pip install wheel
30 python3 -m pip install 'pytest>=6.2.4'
31 python3 -m pip install 'pytest-xdist>=2.3.0'
32 python3 -m pip install 'scapy>=2.4.5'
33 python3 -m pip install xmltodict
34 # Use python2 pip to install older ExaBGP
35 python2 -m pip install 'exabgp<4.0.0'
370c8e07
QY
36 useradd -d /var/run/exabgp/ -s /bin/false exabgp
37
e00241ad
CH
38 # To enable the gRPC topotest install:
39 python3 -m pip install grpcio grpcio-tools
40
c73d2974
KK
41 # Install Socat tool to run PIMv6 tests,
42 # Socat code can be taken from below url,
43 # which has latest changes done for PIMv6,
44 # join and traffic:
45 https://github.com/opensourcerouting/socat/
46
77f3acb4 47
370c8e07
QY
48Enable Coredumps
49""""""""""""""""
50
51Optional, will give better output.
52
53.. code:: shell
54
370c8e07
QY
55 disable apport (which move core files)
56
57Set ``enabled=0`` in ``/etc/default/apport``.
58
59Next, update security limits by changing :file:`/etc/security/limits.conf` to::
60
61 #<domain> <type> <item> <value>
62 * soft core unlimited
63 root soft core unlimited
64 * hard core unlimited
65 root hard core unlimited
66
67Reboot for options to take effect.
68
b638685c
PR
69SNMP Utilities Installation
70"""""""""""""""""""""""""""
71
72To run SNMP test you need to install SNMP utilities and MIBs. Unfortunately
73there are some errors in the upstream MIBS which need to be patched up. The
74following steps will get you there on Ubuntu 20.04.
75
76.. code:: shell
f101a83b 77
b69bc6fe 78 apt install libsnmp-dev
b638685c
PR
79 apt install snmpd snmp
80 apt install snmp-mibs-downloader
81 download-mibs
8709aab5
DA
82 wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/iana/IANA-IPPM-METRICS-REGISTRY-MIB -O /usr/share/snmp/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB
83 wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/ietf/SNMPv2-PDU -O /usr/share/snmp/mibs/ietf/SNMPv2-PDU
84 wget https://raw.githubusercontent.com/FRRouting/frr-mibs/main/ietf/IPATM-IPMC-MIB -O /usr/share/snmp/mibs/ietf/IPATM-IPMC-MIB
b638685c 85 edit /etc/snmp/snmp.conf to look like this
f101a83b
DA
86 # As the snmp packages come without MIB files due to license reasons, loading
87 # of MIBs is disabled by default. If you added the MIBs you can reenable
88 # loading them by commenting out the following line.
b638685c
PR
89 mibs +ALL
90
91
370c8e07
QY
92FRR Installation
93^^^^^^^^^^^^^^^^
94
95FRR needs to be installed separately. It is assume to be configured like the
96standard Ubuntu Packages:
97
98- Binaries in :file:`/usr/lib/frr`
99- State Directory :file:`/var/run/frr`
100- Running under user ``frr``, group ``frr``
101- vtygroup: ``frrvty``
102- config directory: :file:`/etc/frr`
103- For FRR Packages, install the dbg package as well for coredump decoding
104
105No FRR config needs to be done and no FRR daemons should be run ahead of the
106test. They are all started as part of the test.
107
108Manual FRR build
109""""""""""""""""
110
111If you prefer to manually build FRR, then use the following suggested config:
112
113.. code:: shell
114
115 ./configure \
116 --prefix=/usr \
117 --localstatedir=/var/run/frr \
118 --sbindir=/usr/lib/frr \
119 --sysconfdir=/etc/frr \
120 --enable-vtysh \
121 --enable-pimd \
dc935a51 122 --enable-sharpd \
370c8e07
QY
123 --enable-multipath=64 \
124 --enable-user=frr \
125 --enable-group=frr \
126 --enable-vty-group=frrvty \
b638685c 127 --enable-snmp=agentx \
370c8e07
QY
128 --with-pkg-extra-version=-my-manual-build
129
130And create ``frr`` user and ``frrvty`` group as follows:
131
132.. code:: shell
133
134 addgroup --system --gid 92 frr
135 addgroup --system --gid 85 frrvty
136 adduser --system --ingroup frr --home /var/run/frr/ \
137 --gecos "FRRouting suite" --shell /bin/false frr
138 usermod -G frrvty frr
139
140Executing Tests
141---------------
142
fe226e84
CH
143Configure your sudo environment
144^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
145
146Topotests must be run as root. Normally this will be accomplished through the
147use of the ``sudo`` command. In order for topotests to be able to open new
148windows (either XTerm or byobu/screen/tmux windows) certain environment
149variables must be passed through the sudo command. One way to do this is to
e8713b62 150specify the ``-E`` flag to ``sudo``. This will carry over most if not all
fe226e84
CH
151your environment variables include ``PATH``. For example:
152
153.. code:: shell
154
155 sudo -E python3 -m pytest -s -v
156
e8713b62 157If you do not wish to use ``-E`` (e.g., to avoid ``sudo`` inheriting
fe226e84
CH
158``PATH``) you can modify your `/etc/sudoers` config file to specifically pass
159the environment variables required by topotests. Add the following commands to
160your ``/etc/sudoers`` config file.
161
162.. code:: shell
163
164 Defaults env_keep="TMUX"
165 Defaults env_keep+="TMUX_PANE"
166 Defaults env_keep+="STY"
167 Defaults env_keep+="DISPLAY"
168
169If there was already an ``env_keep`` configuration there be sure to use the
170``+=`` rather than ``=`` on the first line above as well.
171
172
77f3acb4
CH
173Execute all tests in distributed test mode
174^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
370c8e07
QY
175
176.. code:: shell
177
fe226e84 178 sudo -E pytest -s -v -nauto --dist=loadfile
370c8e07 179
44f3760c 180The above command must be executed from inside the topotests directory.
181
370c8e07 182All test\_\* scripts in subdirectories are detected and executed (unless
77f3acb4
CH
183disabled in ``pytest.ini`` file). Pytest will execute up to N tests in parallel
184where N is based on the number of cores on the host.
185
186Analyze Test Results (``analyze.py``)
187^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
188
189By default router and execution logs are saved in ``/tmp/topotests`` and an XML
190results file is saved in ``/tmp/topotests.xml``. An analysis tool ``analyze.py``
191is provided to archive and analyze these results after the run completes.
192
193After the test run completes one should pick an archive directory to store the
194results in and pass this value to ``analyze.py``. On first execution the results
195are copied to that directory from ``/tmp``, and subsequent runs use that
196directory for analyzing the results. Below is an example of this which also
197shows the default behavior which is to display all failed and errored tests in
198the run.
199
200.. code:: shell
201
202 ~/frr/tests/topotests# ./analyze.py -Ar run-save
203 bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_converge
204 ospf_basic_functionality/test_ospf_lan.py::test_ospf_lan_tc1_p0
205 bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py::test_BGP_GR_10_p2
206 bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_routingTable
207
208Here we see that 4 tests have failed. We an dig deeper by displaying the
209captured logs and errors. First let's redisplay the results enumerated by adding
e8713b62 210the ``-E`` flag
77f3acb4
CH
211
212.. code:: shell
213
214 ~/frr/tests/topotests# ./analyze.py -Ar run-save -E
215 0 bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_converge
216 1 ospf_basic_functionality/test_ospf_lan.py::test_ospf_lan_tc1_p0
217 2 bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2.py::test_BGP_GR_10_p2
218 3 bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_routingTable
219
220Now to look at the error message for a failed test we use ``-T N`` where N is
221the number of the test we are interested in along with ``--errmsg`` option.
222
223.. code:: shell
224
225 ~/frr/tests/topotests# ./analyze.py -Ar run-save -T0 --errmsg
226 bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_converge: AssertionError: BGP did not converge:
227
228 IPv4 Unicast Summary (VIEW 1):
229 BGP router identifier 172.30.1.1, local AS number 100 vrf-id -1
230 BGP table version 1
231 RIB entries 1, using 184 bytes of memory
232 Peers 3, using 2169 KiB of memory
233
234 Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
235 172.16.1.1 4 65001 0 0 0 0 0 never Connect 0 N/A
236 172.16.1.2 4 65002 0 0 0 0 0 never Connect 0 N/A
237 172.16.1.5 4 65005 0 0 0 0 0 never Connect 0 N/A
238
239 Total number of neighbors 3
240
241 assert False
242
243Now to look at the full text of the error for a failed test we use ``-T N``
244where N is the number of the test we are interested in along with ``--errtext``
245option.
246
247.. code:: shell
248
249 ~/frr/tests/topotests# ./analyze.py -Ar run-save -T0 --errtext
250 bgp_multiview_topo1/test_bgp_multiview_topo1.py::test_bgp_converge: def test_bgp_converge():
251 "Check for BGP converged on all peers and BGP views"
252
253 global fatal_error
254 global net
255 [...]
256 else:
257 # Bail out with error if a router fails to converge
258 bgpStatus = net["r%s" % i].cmd('vtysh -c "show ip bgp view %s summary"' % view)
259 > assert False, "BGP did not converge:\n%s" % bgpStatus
260 E AssertionError: BGP did not converge:
261 E
262 E IPv4 Unicast Summary (VIEW 1):
263 E BGP router identifier 172.30.1.1, local AS number 100 vrf-id -1
264 [...]
265 E Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
266 E 172.16.1.1 4 65001 0 0 0 0 0 never Connect 0 N/A
267 E 172.16.1.2 4 65002 0 0 0 0 0 never Connect 0 N/A
268 [...]
269
270To look at the full capture for a test including the stdout and stderr which
271includes full debug logs, just use the ``-T N`` option without the ``--errmsg``
272or ``--errtext`` options.
273
274.. code:: shell
275
276 ~/frr/tests/topotests# ./analyze.py -Ar run-save -T0
277 @classname: bgp_multiview_topo1.test_bgp_multiview_topo1
278 @name: test_bgp_converge
279 @time: 141.401
280 @message: AssertionError: BGP did not converge:
281 [...]
282 system-out: --------------------------------- Captured Log ---------------------------------
283 2021-08-09 02:55:06,581 DEBUG: lib.micronet_compat.topo: Topo(unnamed): Creating
284 2021-08-09 02:55:06,581 DEBUG: lib.micronet_compat.topo: Topo(unnamed): addHost r1
285 [...]
286 2021-08-09 02:57:16,932 DEBUG: topolog.r1: LinuxNamespace(r1): cmd_status("['/bin/bash', '-c', 'vtysh -c "show ip bgp view 1 summary" 2> /dev/null | grep ^[0-9] | grep -vP " 11\\s+(\\d+)"']", kwargs: {'encoding': 'utf-8', 'stdout': -1, 'stderr': -2, 'shell': False})
287 2021-08-09 02:57:22,290 DEBUG: topolog.r1: LinuxNamespace(r1): cmd_status("['/bin/bash', '-c', 'vtysh -c "show ip bgp view 1 summary" 2> /dev/null | grep ^[0-9] | grep -vP " 11\\s+(\\d+)"']", kwargs: {'encoding': 'utf-8', 'stdout': -1, 'stderr': -2, 'shell': False})
288 2021-08-09 02:57:27,636 DEBUG: topolog.r1: LinuxNamespace(r1): cmd_status("['/bin/bash', '-c', 'vtysh -c "show ip bgp view 1 summary"']", kwargs: {'encoding': 'utf-8', 'stdout': -1, 'stderr': -2, 'shell': False})
289 --------------------------------- Captured Out ---------------------------------
290 system-err: --------------------------------- Captured Err ---------------------------------
370c8e07 291
370c8e07
QY
292
293Execute single test
294^^^^^^^^^^^^^^^^^^^
295
296.. code:: shell
297
298 cd test_to_be_run
a3e8e1aa 299 sudo -E pytest ./test_to_be_run.py
370c8e07 300
44f3760c 301For example, and assuming you are inside the frr directory:
302
303.. code:: shell
304
305 cd tests/topotests/bgp_l3vpn_to_bgp_vrf
a3e8e1aa 306 sudo -E pytest ./test_bgp_l3vpn_to_bgp_vrf.py
44f3760c 307
370c8e07
QY
308For further options, refer to pytest documentation.
309
310Test will set exit code which can be used with ``git bisect``.
311
312For the simulated topology, see the description in the python file.
313
370c8e07
QY
314StdErr log from daemos after exit
315^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
316
317To enable the reporting of any messages seen on StdErr after the daemons exit,
318the following env variable can be set::
319
320 export TOPOTESTS_CHECK_STDERR=Yes
321
e60aaed9
MS
322(The value doesn't matter at this time. The check is whether the env
323variable exists or not.) There is no pass/fail on this reporting; the
324Output will be reported to the console.
370c8e07
QY
325
326Collect Memory Leak Information
327^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
328
e60aaed9
MS
329FRR processes can report unfreed memory allocations upon exit. To
330enable the reporting of memory leaks, define an environment variable
370c8e07
QY
331``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.::
332
333 export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
334
e60aaed9
MS
335This will enable the check and output to console and the writing of
336the information to files with the given prefix (followed by testname),
337ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
338of a memory leak.
370c8e07
QY
339
340Running Topotests with AddressSanitizer
341^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
342
343Topotests can be run with AddressSanitizer. It requires GCC 4.8 or newer.
344(Ubuntu 16.04 as suggested here is fine with GCC 5 as default). For more
345information on AddressSanitizer, see
346https://github.com/google/sanitizers/wiki/AddressSanitizer.
347
348The checks are done automatically in the library call of ``checkRouterRunning``
349(ie at beginning of tests when there is a check for all daemons running). No
350changes or extra configuration for topotests is required beside compiling the
351suite with AddressSanitizer enabled.
352
353If a daemon crashed, then the errorlog is checked for AddressSanitizer output.
354If found, then this is added with context (calling test) to
355:file:`/tmp/AddressSanitizer.txt` in Markdown compatible format.
356
357Compiling for GCC AddressSanitizer requires to use ``gcc`` as a linker as well
358(instead of ``ld``). Here is a suggest way to compile frr with AddressSanitizer
a191719e 359for ``master`` branch:
370c8e07
QY
360
361.. code:: shell
362
363 git clone https://github.com/FRRouting/frr.git
364 cd frr
370c8e07 365 ./bootstrap.sh
4260f165
RZ
366 ./configure \
367 --enable-address-sanitizer \
370c8e07
QY
368 --prefix=/usr/lib/frr --sysconfdir=/etc/frr \
369 --localstatedir=/var/run/frr \
370 --sbindir=/usr/lib/frr --bindir=/usr/lib/frr \
370c8e07
QY
371 --with-moduledir=/usr/lib/frr/modules \
372 --enable-multipath=0 --enable-rtadv \
a191719e
LB
373 --enable-tcp-zebra --enable-fpm --enable-pimd \
374 --enable-sharpd
370c8e07
QY
375 make
376 sudo make install
377 # Create symlink for vtysh, so topotest finds it in /usr/lib/frr
378 sudo ln -s /usr/lib/frr/vtysh /usr/bin/
379
380and create ``frr`` user and ``frrvty`` group as shown above.
381
3f950192
CH
382Debugging Topotest Failures
383^^^^^^^^^^^^^^^^^^^^^^^^^^^
384
77f3acb4
CH
385Install and run tests inside ``tmux`` or ``byobu`` for best results.
386
387``XTerm`` is also fully supported. GNU ``screen`` can be used in most
388situations; however, it does not work as well with launching ``vtysh`` or shell
389on error.
3f950192 390
77f3acb4
CH
391For the below debugging options which launch programs or CLIs, topotest should
392be run within ``tmux`` (or ``screen``)_, as ``gdb``, the shell or ``vtysh`` will
393be launched using that windowing program, otherwise ``xterm`` will be attempted
394to launch the given programs.
3f950192 395
fe226e84
CH
396NOTE: you must run the topotest (pytest) such that your DISPLAY, STY or TMUX
397environment variables are carried over. You can do this by passing the
e8713b62 398``-E`` flag to ``sudo`` or you can modify your ``/etc/sudoers`` config to
fe226e84
CH
399automatically pass that environment variable through to the ``sudo``
400environment.
401
3f950192
CH
402.. _screen: https://www.gnu.org/software/screen/
403.. _tmux: https://github.com/tmux/tmux/wiki
404
c45ef001
CH
405Capturing Packets
406"""""""""""""""""
407
408One can view and capture packets on any of the networks or interfaces defined by
409the topotest by specifying the ``--pcap=NET|INTF|all[,NET|INTF,...]`` CLI option
410as shown in the examples below.
411
412.. code:: shell
413
414 # Capture on all networks in isis_topo1 test
415 sudo -E pytest isis_topo1 --pcap=all
416
417 # Capture on `sw1` network
418 sudo -E pytest isis_topo1 --pcap=sw1
419
420 # Capture on `sw1` network and on interface `eth0` on router `r2`
421 sudo -E pytest isis_topo1 --pcap=sw1,r2:r2-eth0
422
423For each capture a window is opened displaying a live summary of the captured
424packets. Additionally, the entire packet stream is captured in a pcap file in
425the tests log directory e.g.,::
426
427.. code:: console
428
429 $ sudo -E pytest isis_topo1 --pcap=sw1,r2:r2-eth0
430 ...
431 $ ls -l /tmp/topotests/isis_topo1.test_isis_topo1/
432 -rw------- 1 root root 45172 Apr 19 05:30 capture-r2-r2-eth0.pcap
433 -rw------- 1 root root 48412 Apr 19 05:30 capture-sw1.pcap
434 ...
435-
436Viewing Live Daemon Logs
437""""""""""""""""""""""""
438
439One can live view daemon or the frr logs in separate windows using the
440``--logd`` CLI option as shown below.
441
442.. code:: shell
443
444 # View `ripd` logs on all routers in test
445 sudo -E pytest rip_allow_ecmp --logd=ripd
446
447 # View `ripd` logs on all routers and `mgmtd` log on `r1`
448 sudo -E pytest rip_allow_ecmp --logd=ripd --logd=mgmtd,r1
449
450For each capture a window is opened displaying a live summary of the captured
451packets. Additionally, the entire packet stream is captured in a pcap file in
452the tests log directory e.g.,::
453
454When using a unified log file `frr.log` one substitutes `frr` for the daemon
455name in the ``--logd`` CLI option, e.g.,
456
457.. code:: shell
458
459 # View `frr` log on all routers in test
460 sudo -E pytest some_test_suite --logd=frr
461
77f3acb4
CH
462Spawning Debugging CLI, ``vtysh`` or Shells on Routers on Test Failure
463""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
3f950192 464
77f3acb4
CH
465One can have a debugging CLI invoked on test failures by specifying the
466``--cli-on-error`` CLI option as shown in the example below.
3f950192
CH
467
468.. code:: shell
469
fe226e84 470 sudo -E pytest --cli-on-error all-protocol-startup
3f950192 471
77f3acb4
CH
472The debugging CLI can run shell or vtysh commands on any combination of routers
473It can also open shells or vtysh in their own windows for any combination of
474routers. This is usually the most useful option when debugging failures. Here is
475the help command from within a CLI launched on error:
3f950192 476
77f3acb4 477.. code:: shell
3f950192 478
77f3acb4
CH
479 test_bgp_multiview_topo1/test_bgp_routingTable> help
480
c45ef001
CH
481 Basic Commands:
482 cli :: open a secondary CLI window
483 help :: this help
484 hosts :: list hosts
485 quit :: quit the cli
486
487 HOST can be a host or one of the following:
488 - '*' for all hosts
489 - '.' for the parent munet
490 - a regex specified between '/' (e.g., '/rtr.*/')
491
492 New Window Commands:
493 logd HOST [HOST ...] DAEMON :: tail -f on the logfile of the given DAEMON for the given HOST[S]
494 pcap NETWORK :: capture packets from NETWORK into file capture-NETWORK.pcap the command is run within a new window which also shows packet summaries. NETWORK can also be an interface specified as HOST:INTF. To capture inside the host namespace.
495 stderr HOST [HOST ...] DAEMON :: tail -f on the stderr of the given DAEMON for the given HOST[S]
496 stdlog HOST [HOST ...] :: tail -f on the `frr.log` for the given HOST[S]
497 stdout HOST [HOST ...] DAEMON :: tail -f on the stdout of the given DAEMON for the given HOST[S]
498 term HOST [HOST ...] :: open terminal[s] (TMUX or XTerm) on HOST[S], * for all
499 vtysh ROUTER [ROUTER ...] ::
500 xterm HOST [HOST ...] :: open XTerm[s] on HOST[S], * for all
501 Inline Commands:
502 [ROUTER ...] COMMAND :: execute vtysh COMMAND on the router[s]
503 [HOST ...] sh <SHELL-COMMAND> :: execute <SHELL-COMMAND> on hosts
504 [HOST ...] shi <INTERACTIVE-COMMAND> :: execute <INTERACTIVE-COMMAND> on HOST[s]
77f3acb4
CH
505
506 test_bgp_multiview_topo1/test_bgp_routingTable> r1 show int br
507 ------ Host: r1 ------
508 Interface Status VRF Addresses
509 --------- ------ --- ---------
510 erspan0 down default
511 gre0 down default
512 gretap0 down default
513 lo up default
514 r1-eth0 up default 172.16.1.254/24
515 r1-stub up default 172.20.0.1/28
516
517 ----------------------
518 test_bgp_multiview_topo1/test_bgp_routingTable>
519
520Additionally, one can have ``vtysh`` or a shell launched on all routers when a
521test fails. To launch the given process on each router after a test failure
522specify one of ``--shell-on-error`` or ``--vtysh-on-error``.
3f950192 523
77f3acb4
CH
524Spawning ``vtysh`` or Shells on Routers
525"""""""""""""""""""""""""""""""""""""""
3f950192 526
77f3acb4
CH
527Topotest can automatically launch a shell or ``vtysh`` for any or all routers in
528a test. This is enabled by specifying 1 of 2 CLI arguments ``--shell`` or
529``--vtysh``. Both of these options can be set to a single router value, multiple
530comma-seperated values, or ``all``.
3f950192 531
77f3acb4
CH
532When either of these options are specified topotest will pause after setup and
533each test to allow for inspection of the router state.
3f950192 534
77f3acb4 535Here's an example of launching ``vtysh`` on routers ``rt1`` and ``rt2``.
3f950192
CH
536
537.. code:: shell
538
fe226e84 539 sudo -E pytest --vtysh=rt1,rt2 all-protocol-startup
3f950192
CH
540
541Debugging with GDB
542""""""""""""""""""
543
544Topotest can automatically launch any daemon with ``gdb``, possibly setting
545breakpoints for any test run. This is enabled by specifying 1 or 2 CLI arguments
546``--gdb-routers`` and ``--gdb-daemons``. Additionally ``--gdb-breakpoints`` can
547be used to automatically set breakpoints in the launched ``gdb`` processes.
548
549Each of these options can be set to a single value, multiple comma-seperated
550values, or ``all``. If ``--gdb-routers`` is empty but ``--gdb_daemons`` is set
551then the given daemons will be launched in ``gdb`` on all routers in the test.
552Likewise if ``--gdb_routers`` is set, but ``--gdb_daemons`` is empty then all
553daemons on the given routers will be launched in ``gdb``.
554
555Here's an example of launching ``zebra`` and ``bgpd`` inside ``gdb`` on router
556``r1`` with a breakpoint set on ``nb_config_diff``
557
558.. code:: shell
559
fe226e84 560 sudo -E pytest --gdb-routers=r1 \
3f950192
CH
561 --gdb-daemons=bgpd,zebra \
562 --gdb-breakpoints=nb_config_diff \
563 all-protocol-startup
564
e58133a7
CH
565Detecting Memleaks with Valgrind
566""""""""""""""""""""""""""""""""
567
568Topotest can automatically launch all daemons with ``valgrind`` to check for
569memleaks. This is enabled by specifying 1 or 2 CLI arguments.
570``--valgrind-memleaks`` will enable general memleak detection, and
571``--valgrind-extra`` enables extra functionality including generating a
572suppression file. The suppression file ``tools/valgrind.supp`` is used when
573memleak detection is enabled.
574
575.. code:: shell
576
fe226e84 577 sudo -E pytest --valgrind-memleaks all-protocol-startup
e58133a7 578
a3e8e1aa
CH
579Collecting Performance Data using perf(1)
580"""""""""""""""""""""""""""""""""""""""""
581
582Topotest can automatically launch any daemon under ``perf(1)`` to collect
583performance data. The daemon is run in non-daemon mode with ``perf record -g``.
584The ``perf.data`` file will be saved in the router specific directory under the
585tests run directoy.
586
587Here's an example of collecting performance data from ``mgmtd`` on router ``r1``
588during the config_timing test.
589
590.. code:: console
591
592 $ sudo -E pytest --perf=mgmtd,r1 config_timing
593 ...
594 $ find /tmp/topotests/ -name '*perf.data*'
595 /tmp/topotests/config_timing.test_config_timing/r1/perf.data
596
597To specify different arguments for ``perf record``, one can use the
598``--perf-options`` this will replace the ``-g`` used by default.
599
370c8e07
QY
600.. _topotests_docker:
601
602Running Tests with Docker
603-------------------------
604
605There is a Docker image which allows to run topotests.
606
607Quickstart
608^^^^^^^^^^
609
610If you have Docker installed, you can run the topotests in Docker. The easiest
611way to do this, is to use the make targets from this repository.
612
613Your current user needs to have access to the Docker daemon. Alternatively you
614can run these commands as root.
615
616.. code:: console
617
618 make topotests
619
620This command will pull the most recent topotests image from Dockerhub, compile
621FRR inside of it, and run the topotests.
622
623Advanced Usage
624^^^^^^^^^^^^^^
625
626Internally, the topotests make target uses a shell script to pull the image and
627spawn the Docker container.
628
629There are several environment variables which can be used to modify the
630behavior of the script, these can be listed by calling it with ``-h``:
631
632.. code:: console
633
634 ./tests/topotests/docker/frr-topotests.sh -h
635
636For example, a volume is used to cache build artifacts between multiple runs of
637the image. If you need to force a complete recompile, you can set
638``TOPOTEST_CLEAN``:
639
640.. code:: console
641
642 TOPOTEST_CLEAN=1 ./tests/topotests/docker/frr-topotests.sh
643
644By default, ``frr-topotests.sh`` will build frr and run pytest. If you append
645arguments and the first one starts with ``/`` or ``./``, they will replace the
646call to pytest. If the appended arguments do not match this patttern, they will
647be provided to pytest as arguments. So, to run a specific test with more
648verbose logging:
649
650.. code:: console
651
652 ./tests/topotests/docker/frr-topotests.sh -vv -s all-protocol-startup/test_all_protocol_startup.py
653
654And to compile FRR but drop into a shell instead of running pytest:
655
656.. code:: console
657
658 ./tests/topotests/docker/frr-topotests.sh /bin/bash
659
660Development
661^^^^^^^^^^^
662
663The Docker image just includes all the components to run the topotests, but not
664the topotests themselves. So if you just want to write tests and don't want to
665make changes to the environment provided by the Docker image. You don't need to
666build your own Docker image if you do not want to.
667
668When developing new tests, there is one caveat though: The startup script of
669the container will run a ``git-clean`` on its copy of the FRR tree to avoid any
670pollution of the container with build artefacts from the host. This will also
671result in your newly written tests being unavailable in the container unless at
672least added to the index with ``git-add``.
673
674If you do want to test changes to the Docker image, you can locally build the
675image and run the tests without pulling from the registry using the following
676commands:
677
678.. code:: console
679
680 make topotests-build
681 TOPOTEST_PULL=0 make topotests
682
683
684.. _topotests-guidelines:
685
686Guidelines
687----------
688
689Executing Tests
690^^^^^^^^^^^^^^^
691
692To run the whole suite of tests the following commands must be executed at the
693top level directory of topotest:
694
695.. code:: shell
696
697 $ # Change to the top level directory of topotests.
698 $ cd path/to/topotests
77f3acb4 699 $ # Tests must be run as root, since micronet requires it.
fe226e84 700 $ sudo -E pytest
370c8e07
QY
701
702In order to run a specific test, you can use the following command:
703
704.. code:: shell
705
706 $ # running a specific topology
fe226e84 707 $ sudo -E pytest ospf-topo1/
370c8e07
QY
708 $ # or inside the test folder
709 $ cd ospf-topo1
fe226e84
CH
710 $ sudo -E pytest # to run all tests inside the directory
711 $ sudo -E pytest test_ospf_topo1.py # to run a specific test
370c8e07
QY
712 $ # or outside the test folder
713 $ cd ..
fe226e84 714 $ sudo -E pytest ospf-topo1/test_ospf_topo1.py # to run a specific one
370c8e07
QY
715
716The output of the tested daemons will be available at the temporary folder of
717your machine:
718
719.. code:: shell
720
721 $ ls /tmp/topotest/ospf-topo1.test_ospf-topo1/r1
722 ...
723 zebra.err # zebra stderr output
724 zebra.log # zebra log file
725 zebra.out # zebra stdout output
726 ...
727
728You can also run memory leak tests to get reports:
729
730.. code:: shell
731
732 $ # Set the environment variable to apply to a specific test...
fe226e84 733 $ sudo -E env TOPOTESTS_CHECK_MEMLEAK="/tmp/memleak_report_" pytest ospf-topo1/test_ospf_topo1.py
370c8e07
QY
734 $ # ...or apply to all tests adding this line to the configuration file
735 $ echo 'memleak_path = /tmp/memleak_report_' >> pytest.ini
736 $ # You can also use your editor
737 $ $EDITOR pytest.ini
738 $ # After running tests you should see your files:
739 $ ls /tmp/memleak_report_*
740 memleak_report_test_ospf_topo1.txt
741
742Writing a New Test
743^^^^^^^^^^^^^^^^^^
744
745This section will guide you in all recommended steps to produce a standard
746topology test.
747
748This is the recommended test writing routine:
749
750- Write a topology (Graphviz recommended)
751- Obtain configuration files
752- Write the test itself
9dd78258 753- Format the new code using `black <https://github.com/psf/black>`_
370c8e07
QY
754- Create a Pull Request
755
b43be6b8
MS
756Some things to keep in mind:
757
758- BGP tests MUST use generous convergence timeouts - you must ensure
759 that any test involving BGP uses a convergence timeout of at least
760 130 seconds.
761- Topotests are run on a range of Linux versions: if your test
762 requires some OS-specific capability (like mpls support, or vrf
763 support), there are test functions available in the libraries that
764 will help you determine whether your test should run or be skipped.
765- Avoid including unstable data in your test: don't rely on link-local
766 addresses or ifindex values, for example, because these can change
767 from run to run.
77f3acb4
CH
768- Using sleep is almost never appropriate. As an example: if the test resets the
769 peers in BGP, the test should look for the peers re-converging instead of just
770 sleeping an arbitrary amount of time and continuing on. See
fe226e84
CH
771 ``verify_bgp_convergence`` as a good example of this. In particular look at
772 it's use of the ``@retry`` decorator. If you are having troubles figuring out
773 what to look for, please do not be afraid to ask.
77f3acb4 774- Don't duplicate effort. There exists many protocol utility functions that can
fe226e84
CH
775 be found in their eponymous module under ``tests/topotests/lib/`` (e.g.,
776 ``ospf.py``)
77f3acb4 777
d9c43f8f 778
d9c43f8f 779
370c8e07
QY
780Topotest File Hierarchy
781"""""""""""""""""""""""
782
783Before starting to write any tests one must know the file hierarchy. The
784repository hierarchy looks like this:
785
786.. code:: shell
787
788 $ cd path/to/topotest
789 $ find ./*
790 ...
791 ./README.md # repository read me
792 ./GUIDELINES.md # this file
793 ./conftest.py # test hooks - pytest related functions
794 ./example-test # example test folder
795 ./example-test/__init__.py # python package marker - must always exist.
796 ./example-test/test_template.jpg # generated topology picture - see next section
797 ./example-test/test_template.dot # Graphviz dot file
798 ./example-test/test_template.py # the topology plus the test
799 ...
800 ./ospf-topo1 # the ospf topology test
801 ./ospf-topo1/r1 # router 1 configuration files
802 ./ospf-topo1/r1/zebra.conf # zebra configuration file
803 ./ospf-topo1/r1/ospfd.conf # ospf configuration file
804 ./ospf-topo1/r1/ospfroute.txt # 'show ip ospf' output reference file
805 # removed other for shortness sake
806 ...
807 ./lib # shared test/topology functions
808 ./lib/topogen.py # topogen implementation
809 ./lib/topotest.py # topotest implementation
810
811Guidelines for creating/editing topotest:
812
813- New topologies that don't fit the existing directories should create its own
814- Always remember to add the ``__init__.py`` to new folders, this makes auto
815 complete engines and pylint happy
816- Router (Quagga/FRR) specific code should go on topotest.py
817- Generic/repeated router actions should have an abstraction in
818 topogen.TopoRouter.
819- Generic/repeated non-router code should go to topotest.py
820- pytest related code should go to conftest.py (e.g. specialized asserts)
821
822Defining the Topology
823"""""""""""""""""""""
824
825The first step to write a new test is to define the topology. This step can be
826done in many ways, but the recommended is to use Graphviz to generate a drawing
827of the topology. It allows us to see the topology graphically and to see the
56f0bea7 828names of equipment, links and addresses.
370c8e07
QY
829
830Here is an example of Graphviz dot file that generates the template topology
831:file:`tests/topotests/example-test/test_template.dot` (the inlined code might
832get outdated, please see the linked file)::
833
834 graph template {
835 label="template";
836
837 # Routers
838 r1 [
839 shape=doubleoctagon,
840 label="r1",
841 fillcolor="#f08080",
842 style=filled,
843 ];
844 r2 [
845 shape=doubleoctagon,
846 label="r2",
847 fillcolor="#f08080",
848 style=filled,
849 ];
850
851 # Switches
852 s1 [
853 shape=oval,
854 label="s1\n192.168.0.0/24",
855 fillcolor="#d0e0d0",
856 style=filled,
857 ];
858 s2 [
859 shape=oval,
860 label="s2\n192.168.1.0/24",
861 fillcolor="#d0e0d0",
862 style=filled,
863 ];
864
865 # Connections
866 r1 -- s1 [label="eth0\n.1"];
867
868 r1 -- s2 [label="eth1\n.100"];
869 r2 -- s2 [label="eth0\n.1"];
870 }
871
872Here is the produced graph:
873
874.. graphviz::
875
876 graph template {
877 label="template";
878
879 # Routers
880 r1 [
881 shape=doubleoctagon,
882 label="r1",
883 fillcolor="#f08080",
884 style=filled,
885 ];
886 r2 [
887 shape=doubleoctagon,
888 label="r2",
889 fillcolor="#f08080",
890 style=filled,
891 ];
892
893 # Switches
894 s1 [
895 shape=oval,
896 label="s1\n192.168.0.0/24",
897 fillcolor="#d0e0d0",
898 style=filled,
899 ];
900 s2 [
901 shape=oval,
902 label="s2\n192.168.1.0/24",
903 fillcolor="#d0e0d0",
904 style=filled,
905 ];
906
907 # Connections
908 r1 -- s1 [label="eth0\n.1"];
909
910 r1 -- s2 [label="eth1\n.100"];
911 r2 -- s2 [label="eth0\n.1"];
912 }
913
914Generating / Obtaining Configuration Files
915""""""""""""""""""""""""""""""""""""""""""
916
917In order to get the configuration files or command output for each router, we
918need to run the topology and execute commands in ``vtysh``. The quickest way to
919achieve that is writing the topology building code and running the topology.
920
921To bootstrap your test topology, do the following steps:
922
923- Copy the template test
924
925.. code:: shell
926
927 $ mkdir new-topo/
928 $ touch new-topo/__init__.py
929 $ cp example-test/test_template.py new-topo/test_new_topo.py
930
931- Modify the template according to your dot file
932
933Here is the template topology described in the previous section in python code:
934
935.. code:: py
936
77f3acb4
CH
937 topodef = {
938 "s1": "r1"
939 "s2": ("r1", "r2")
940 }
941
942If more specialized topology definitions, or router initialization arguments are
943required a build function can be used instead of a dictionary:
944
945.. code:: py
946
947 def build_topo(tgen):
948 "Build function"
370c8e07 949
77f3acb4
CH
950 # Create 2 routers
951 for routern in range(1, 3):
952 tgen.add_router("r{}".format(routern))
370c8e07 953
77f3acb4
CH
954 # Create a switch with just one router connected to it to simulate a
955 # empty network.
956 switch = tgen.add_switch("s1")
957 switch.add_link(tgen.gears["r1"])
370c8e07 958
77f3acb4
CH
959 # Create a connection between r1 and r2
960 switch = tgen.add_switch("s2")
961 switch.add_link(tgen.gears["r1"])
962 switch.add_link(tgen.gears["r2"])
370c8e07
QY
963
964- Run the topology
965
966Topogen allows us to run the topology without running any tests, you can do
967that using the following example commands:
968
969.. code:: shell
970
971 $ # Running your bootstraped topology
fe226e84 972 $ sudo -E pytest -s --topology-only new-topo/test_new_topo.py
370c8e07 973 $ # Running the test_template.py topology
fe226e84 974 $ sudo -E pytest -s --topology-only example-test/test_template.py
370c8e07 975 $ # Running the ospf_topo1.py topology
fe226e84 976 $ sudo -E pytest -s --topology-only ospf-topo1/test_ospf_topo1.py
370c8e07
QY
977
978Parameters explanation:
979
980.. program:: pytest
981
982.. option:: -s
983
77f3acb4
CH
984 Actives input/output capture. If this is not specified a new window will be
985 opened for the interactive CLI, otherwise it will be activated inline.
370c8e07
QY
986
987.. option:: --topology-only
988
989 Don't run any tests, just build the topology.
990
991After executing the commands above, you should get the following terminal
992output:
993
994.. code:: shell
995
fe226e84 996 frr/tests/topotests# sudo -E pytest -s --topology-only ospf_topo1/test_ospf_topo1.py
77f3acb4
CH
997 ============================= test session starts ==============================
998 platform linux -- Python 3.9.2, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
999 rootdir: /home/chopps/w/frr/tests/topotests, configfile: pytest.ini
1000 plugins: forked-1.3.0, xdist-2.3.0
1001 collected 11 items
370c8e07 1002
77f3acb4
CH
1003 [...]
1004 unet>
370c8e07 1005
77f3acb4
CH
1006The last line shows us that we are now using the CLI (Command Line
1007Interface), from here you can call your router ``vtysh`` or even bash.
370c8e07 1008
77f3acb4 1009Here's the help text:
370c8e07 1010
77f3acb4 1011.. code:: shell
e8713b62 1012
77f3acb4 1013 unet> help
370c8e07 1014
77f3acb4
CH
1015 Commands:
1016 help :: this help
1017 sh [hosts] <shell-command> :: execute <shell-command> on <host>
1018 term [hosts] :: open shell terminals for hosts
1019 vtysh [hosts] :: open vtysh terminals for hosts
1020 [hosts] <vtysh-command> :: execute vtysh-command on hosts
370c8e07 1021
77f3acb4 1022Here are some commands example:
370c8e07 1023
77f3acb4 1024.. code:: shell
370c8e07 1025
77f3acb4
CH
1026 unet> sh r1 ping 10.0.3.1
1027 PING 10.0.3.1 (10.0.3.1) 56(84) bytes of data.
1028 64 bytes from 10.0.3.1: icmp_seq=1 ttl=64 time=0.576 ms
1029 64 bytes from 10.0.3.1: icmp_seq=2 ttl=64 time=0.083 ms
1030 64 bytes from 10.0.3.1: icmp_seq=3 ttl=64 time=0.088 ms
1031 ^C
1032 --- 10.0.3.1 ping statistics ---
1033 3 packets transmitted, 3 received, 0% packet loss, time 1998ms
1034 rtt min/avg/max/mdev = 0.083/0.249/0.576/0.231 ms
1035
1036 unet> r1 show run
1037 Building configuration...
1038
1039 Current configuration:
1040 !
1041 frr version 8.1-dev-my-manual-build
1042 frr defaults traditional
1043 hostname r1
1044 log file /tmp/topotests/ospf_topo1.test_ospf_topo1/r1/zebra.log
1045 [...]
1046 end
1047
1048 unet> show daemons
1049 ------ Host: r1 ------
1050 zebra ospfd ospf6d staticd
1051 ------- End: r1 ------
1052 ------ Host: r2 ------
1053 zebra ospfd ospf6d staticd
1054 ------- End: r2 ------
1055 ------ Host: r3 ------
1056 zebra ospfd ospf6d staticd
1057 ------- End: r3 ------
1058 ------ Host: r4 ------
1059 zebra ospfd ospf6d staticd
1060 ------- End: r4 ------
370c8e07
QY
1061
1062After you successfully configured your topology, you can obtain the
1063configuration files (per-daemon) using the following commands:
1064
1065.. code:: shell
1066
77f3acb4 1067 unet> sh r3 vtysh -d ospfd
370c8e07
QY
1068
1069 Hello, this is FRRouting (version 3.1-devrzalamena-build).
1070 Copyright 1996-2005 Kunihiro Ishiguro, et al.
1071
77f3acb4 1072 r1# show running-config
370c8e07
QY
1073 Building configuration...
1074
1075 Current configuration:
1076 !
1077 frr version 3.1-devrzalamena-build
1078 frr defaults traditional
1079 no service integrated-vtysh-config
1080 !
1081 log file ospfd.log
1082 !
1083 router ospf
1084 ospf router-id 10.0.255.3
1085 redistribute kernel
1086 redistribute connected
1087 redistribute static
1088 network 10.0.3.0/24 area 0
1089 network 10.0.10.0/24 area 0
1090 network 172.16.0.0/24 area 1
1091 !
1092 line vty
1093 !
1094 end
77f3acb4 1095 r1#
370c8e07 1096
e03862c3
HS
1097You can also login to the node specified by nsenter using bash, etc.
1098A pid file for each node will be created in the relevant test dir.
1099You can run scripts inside the node, or use vtysh's <tab> or <?> feature.
1100
1101.. code:: shell
1102
1103 [unet shell]
1104 # cd tests/topotests/srv6_locator
1105 # ./test_srv6_locator.py --topology-only
1994c6bc 1106 unet> r1 show segment-routing srv6 locator
e03862c3
HS
1107 Locator:
1108 Name ID Prefix Status
1109 -------------------- ------- ------------------------ -------
1110 loc1 1 2001:db8:1:1::/64 Up
1111 loc2 2 2001:db8:2:2::/64 Up
1112
1113 [Another shell]
1114 # nsenter -a -t $(cat /tmp/topotests/srv6_locator.test_srv6_locator/r1.pid) bash --norc
1115 # vtysh
1994c6bc 1116 r1# r1 show segment-routing srv6 locator
e03862c3
HS
1117 Locator:
1118 Name ID Prefix Status
1119 -------------------- ------- ------------------------ -------
1120 loc1 1 2001:db8:1:1::/64 Up
1121 loc2 2 2001:db8:2:2::/64 Up
1122
370c8e07
QY
1123Writing Tests
1124"""""""""""""
1125
1126Test topologies should always be bootstrapped from
9b6f04c0 1127:file:`tests/topotests/example_test/test_template.py` because it contains
370c8e07
QY
1128important boilerplate code that can't be avoided, like:
1129
370c8e07
QY
1130Example:
1131
1132.. code:: py
1133
9b6f04c0
CH
1134 # For all routers arrange for:
1135 # - starting zebra using config file from <rtrname>/zebra.conf
1136 # - starting ospfd using an empty config file.
1137 for rname, router in router_list.items():
1138 router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
1139 router.load_config(TopoRouter.RD_OSPF)
1140
370c8e07 1141
77f3acb4 1142- The topology definition or build function
370c8e07
QY
1143
1144.. code:: py
1145
77f3acb4
CH
1146 topodef = {
1147 "s1": ("r1", "r2"),
1148 "s2": ("r2", "r3")
1149 }
1150
1151 def build_topo(tgen):
370c8e07 1152 # topology build code
77f3acb4 1153 ...
370c8e07 1154
fe226e84
CH
1155- pytest setup/teardown fixture to start the topology and supply ``tgen``
1156 argument to tests.
370c8e07
QY
1157
1158.. code:: py
1159
9b6f04c0
CH
1160
1161 @pytest.fixture(scope="module")
1162 def tgen(request):
1163 "Setup/Teardown the environment and provide tgen argument to tests"
1164
77f3acb4
CH
1165 tgen = Topogen(topodef, module.__name__)
1166 # or
1167 tgen = Topogen(build_topo, module.__name__)
1168
9b6f04c0 1169 ...
370c8e07 1170
9b6f04c0
CH
1171 # Start and configure the router daemons
1172 tgen.start_router()
370c8e07 1173
9b6f04c0
CH
1174 # Provide tgen as argument to each test function
1175 yield tgen
370c8e07 1176
9b6f04c0
CH
1177 # Teardown after last test runs
1178 tgen.stop_topology()
370c8e07 1179
370c8e07
QY
1180
1181Requirements:
1182
f101a83b 1183- Directory name for a new topotest must not contain hyphen (``-``) characters.
6ab47b03 1184 To separate words, use underscores (``_``). For example, ``tests/topotests/bgp_new_example``.
370c8e07
QY
1185- Test code should always be declared inside functions that begin with the
1186 ``test_`` prefix. Functions beginning with different prefixes will not be run
1187 by pytest.
1188- Configuration files and long output commands should go into separated files
1189 inside folders named after the equipment.
1190- Tests must be able to run without any interaction. To make sure your test
1191 conforms with this, run it without the :option:`-s` parameter.
9dd78258
DA
1192- Use `black <https://github.com/psf/black>`_ code formatter before creating
1193 a pull request. This ensures we have a unified code style.
0f84d138 1194- Mark test modules with pytest markers depending on the daemons used during the
b43be6b8 1195 tests (see :ref:`topotests-markers`)
c2f24d8e
DA
1196- Always use IPv4 :rfc:`5737` (``192.0.2.0/24``, ``198.51.100.0/24``,
1197 ``203.0.113.0/24``) and IPv6 :rfc:`3849` (``2001:db8::/32``) ranges reserved
1198 for documentation.
370c8e07
QY
1199
1200Tips:
1201
1202- Keep results in stack variables, so people inspecting code with ``pdb`` can
1203 easily print their values.
1204
1205Don't do this:
1206
1207.. code:: py
1208
1209 assert foobar(router1, router2)
1210
1211Do this instead:
1212
1213.. code:: py
1214
1215 result = foobar(router1, router2)
1216 assert result
1217
1218- Use ``assert`` messages to indicate where the test failed.
1219
1220Example:
1221
1222.. code:: py
1223
1224 for router in router_list:
1225 # ...
1226 assert condition, 'Router "{}" condition failed'.format(router.name)
1227
1228Debugging Execution
1229^^^^^^^^^^^^^^^^^^^
1230
1231The most effective ways to inspect topology tests are:
1232
1233- Run pytest with ``--pdb`` option. This option will cause a pdb shell to
1234 appear when an assertion fails
1235
1236Example: ``pytest -s --pdb ospf-topo1/test_ospf_topo1.py``
1237
1238- Set a breakpoint in the test code with ``pdb``
1239
1240Example:
1241
1242.. code:: py
1243
1244 # Add the pdb import at the beginning of the file
1245 import pdb
1246 # ...
1247
1248 # Add a breakpoint where you think the problem is
1249 def test_bla():
1250 # ...
1251 pdb.set_trace()
1252 # ...
1253
1254The `Python Debugger <https://docs.python.org/2.7/library/pdb.html>`__ (pdb)
1255shell allows us to run many useful operations like:
1256
1257- Setting breaking point on file/function/conditions (e.g. ``break``,
1258 ``condition``)
1259- Inspecting variables (e.g. ``p`` (print), ``pp`` (pretty print))
1260- Running python code
1261
1262.. tip::
1263
1264 The TopoGear (equipment abstraction class) implements the ``__str__`` method
1265 that allows the user to inspect equipment information.
1266
1267Example of pdb usage:
1268
1269.. code:: shell
1270
1271 > /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(121)test_ospf_convergence()
1272 -> for rnum in range(1, 5):
1273 (Pdb) help
1274 Documented commands (type help <topic>):
1275 ========================================
1276 EOF bt cont enable jump pp run unt
1277 a c continue exit l q s until
1278 alias cl d h list quit step up
1279 args clear debug help n r tbreak w
1280 b commands disable ignore next restart u whatis
1281 break condition down j p return unalias where
1282
1283 Miscellaneous help topics:
1284 ==========================
1285 exec pdb
1286
1287 Undocumented commands:
1288 ======================
1289 retval rv
1290
1291 (Pdb) list
1292 116 title2="Expected output")
1293 117
1294 118 def test_ospf_convergence():
1295 119 "Test OSPF daemon convergence"
1296 120 pdb.set_trace()
1297 121 -> for rnum in range(1, 5):
1298 122 router = 'r{}'.format(rnum)
1299 123
1300 124 # Load expected results from the command
1301 125 reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
1302 126 expected = open(reffile).read()
1303 (Pdb) step
1304 > /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(122)test_ospf_convergence()
1305 -> router = 'r{}'.format(rnum)
1306 (Pdb) step
1307 > /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(125)test_ospf_convergence()
1308 -> reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
1309 (Pdb) print rnum
1310 1
1311 (Pdb) print router
1312 r1
1313 (Pdb) tgen = get_topogen()
1314 (Pdb) pp tgen.gears[router]
1315 <lib.topogen.TopoRouter object at 0x7f74e06c9850>
1316 (Pdb) pp str(tgen.gears[router])
1317 'TopoGear<name="r1",links=["r1-eth0"<->"s1-eth0","r1-eth1"<->"s3-eth0"]> TopoRouter<>'
1318 (Pdb) l 125
1319 120 pdb.set_trace()
1320 121 for rnum in range(1, 5):
1321 122 router = 'r{}'.format(rnum)
1322 123
1323 124 # Load expected results from the command
1324 125 -> reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
1325 126 expected = open(reffile).read()
1326 127
1327 128 # Run test function until we get an result. Wait at most 60 seconds.
1328 129 test_func = partial(compare_show_ip_ospf, router, expected)
1329 130 result, diff = topotest.run_and_expect(test_func, '',
1330 (Pdb) router1 = tgen.gears[router]
1331 (Pdb) router1.vtysh_cmd('show ip ospf route')
1332 '============ OSPF network routing table ============\r\nN 10.0.1.0/24 [10] area: 0.0.0.0\r\n directly attached to r1-eth0\r\nN 10.0.2.0/24 [20] area: 0.0.0.0\r\n via 10.0.3.3, r1-eth1\r\nN 10.0.3.0/24 [10] area: 0.0.0.0\r\n directly attached to r1-eth1\r\nN 10.0.10.0/24 [20] area: 0.0.0.0\r\n via 10.0.3.1, r1-eth1\r\nN IA 172.16.0.0/24 [20] area: 0.0.0.0\r\n via 10.0.3.1, r1-eth1\r\nN IA 172.16.1.0/24 [30] area: 0.0.0.0\r\n via 10.0.3.1, r1-eth1\r\n\r\n============ OSPF router routing table =============\r\nR 10.0.255.2 [10] area: 0.0.0.0, ASBR\r\n via 10.0.3.3, r1-eth1\r\nR 10.0.255.3 [10] area: 0.0.0.0, ABR, ASBR\r\n via 10.0.3.1, r1-eth1\r\nR 10.0.255.4 IA [20] area: 0.0.0.0, ASBR\r\n via 10.0.3.1, r1-eth1\r\n\r\n============ OSPF external routing table ===========\r\n\r\n\r\n'
77f3acb4
CH
1333 (Pdb) tgen.cli()
1334 unet>
370c8e07 1335
77f3acb4 1336To enable more debug messages in other Topogen subsystems, more
370c8e07
QY
1337logging messages can be displayed by modifying the test configuration file
1338``pytest.ini``:
1339
1340.. code:: ini
1341
1342 [topogen]
1343 # Change the default verbosity line from 'info'...
1344 #verbosity = info
1345 # ...to 'debug'
1346 verbosity = debug
1347
1348Instructions for use, write or debug topologies can be found in :ref:`topotests-guidelines`.
1349To learn/remember common code snippets see :ref:`topotests-snippets`.
1350
1351Before creating a new topology, make sure that there isn't one already that
1352does what you need. If nothing is similar, then you may create a new topology,
1353preferably, using the newest template
1354(:file:`tests/topotests/example-test/test_template.py`).
1355
0f84d138
DS
1356.. include:: topotests-markers.rst
1357
370c8e07
QY
1358.. include:: topotests-snippets.rst
1359
1360License
1361-------
1362
1363All the configs and scripts are licensed under a ISC-style license. See Python
1364scripts for details.