]> git.proxmox.com Git - mirror_frr.git/blob - doc/developer/topotests.rst
Merge pull request #7764 from pguibert6WIND/nhrp_shortcut_routes
[mirror_frr.git] / doc / developer / topotests.rst
1 .. _topotests:
2
3 Topotests
4 =========
5
6 Topotests is a suite of topology tests for FRR built on top of Mininet.
7
8 Installation and Setup
9 ----------------------
10
11 Only tested with Ubuntu 16.04 and Ubuntu 18.04 (which uses Mininet 2.2.x).
12
13 Instructions are the same for all setups (i.e. ExaBGP is only used for BGP
14 tests).
15
16 Installing Mininet Infrastructure
17 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18
19 .. code:: shell
20
21 apt-get install mininet
22 apt-get install python-pip
23 apt-get install iproute
24 apt-get install iperf
25 pip install ipaddr
26 pip install "pytest<5"
27 pip install "scapy>=2.4.2"
28 pip install exabgp==3.4.17 (Newer 4.0 version of exabgp is not yet
29 supported)
30 useradd -d /var/run/exabgp/ -s /bin/false exabgp
31
32 Enable Coredumps
33 """"""""""""""""
34
35 Optional, will give better output.
36
37 .. code:: shell
38
39 apt-get install gdb
40 disable apport (which move core files)
41
42 Set ``enabled=0`` in ``/etc/default/apport``.
43
44 Next, update security limits by changing :file:`/etc/security/limits.conf` to::
45
46 #<domain> <type> <item> <value>
47 * soft core unlimited
48 root soft core unlimited
49 * hard core unlimited
50 root hard core unlimited
51
52 Reboot for options to take effect.
53
54 SNMP Utilities Installation
55 """""""""""""""""""""""""""
56
57 To run SNMP test you need to install SNMP utilities and MIBs. Unfortunately
58 there are some errors in the upstream MIBS which need to be patched up. The
59 following steps will get you there on Ubuntu 20.04.
60
61 .. code:: shell
62
63 apt install snmpd snmp
64 apt install snmp-mibs-downloader
65 download-mibs
66 wget http://www.iana.org/assignments/ianaippmmetricsregistry-mib/ianaippmmetricsregistry-mib -O /usr/share/snmp/mibs/iana/IANA-IPPM-METRICS-REGISTRY-MIB
67 wget http://pastebin.com/raw.php?i=p3QyuXzZ -O /usr/share/snmp/mibs/ietf/SNMPv2-PDU
68 wget http://pastebin.com/raw.php?i=gG7j8nyk -O /usr/share/snmp/mibs/ietf/IPATM-IPMC-MIB
69 edit /etc/snmp/snmp.conf to look like this
70 # As the snmp packages come without MIB files due to license reasons, loading
71 # of MIBs is disabled by default. If you added the MIBs you can reenable
72 # loading them by commenting out the following line.
73 mibs +ALL
74
75
76 FRR Installation
77 ^^^^^^^^^^^^^^^^
78
79 FRR needs to be installed separately. It is assume to be configured like the
80 standard Ubuntu Packages:
81
82 - Binaries in :file:`/usr/lib/frr`
83 - State Directory :file:`/var/run/frr`
84 - Running under user ``frr``, group ``frr``
85 - vtygroup: ``frrvty``
86 - config directory: :file:`/etc/frr`
87 - For FRR Packages, install the dbg package as well for coredump decoding
88
89 No FRR config needs to be done and no FRR daemons should be run ahead of the
90 test. They are all started as part of the test.
91
92 Manual FRR build
93 """"""""""""""""
94
95 If you prefer to manually build FRR, then use the following suggested config:
96
97 .. code:: shell
98
99 ./configure \
100 --prefix=/usr \
101 --localstatedir=/var/run/frr \
102 --sbindir=/usr/lib/frr \
103 --sysconfdir=/etc/frr \
104 --enable-vtysh \
105 --enable-pimd \
106 --enable-sharpd \
107 --enable-multipath=64 \
108 --enable-user=frr \
109 --enable-group=frr \
110 --enable-vty-group=frrvty \
111 --enable-snmp=agentx \
112 --with-pkg-extra-version=-my-manual-build
113
114 And create ``frr`` user and ``frrvty`` group as follows:
115
116 .. code:: shell
117
118 addgroup --system --gid 92 frr
119 addgroup --system --gid 85 frrvty
120 adduser --system --ingroup frr --home /var/run/frr/ \
121 --gecos "FRRouting suite" --shell /bin/false frr
122 usermod -G frrvty frr
123
124 Executing Tests
125 ---------------
126
127 Execute all tests with output to console
128 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
129
130 .. code:: shell
131
132 py.test -s -v --tb=no
133
134 The above command must be executed from inside the topotests directory.
135
136 All test\_\* scripts in subdirectories are detected and executed (unless
137 disabled in ``pytest.ini`` file).
138
139 ``--tb=no`` disables the python traceback which might be irrelevant unless the
140 test script itself is debugged.
141
142 Execute single test
143 ^^^^^^^^^^^^^^^^^^^
144
145 .. code:: shell
146
147 cd test_to_be_run
148 ./test_to_be_run.py
149
150 For example, and assuming you are inside the frr directory:
151
152 .. code:: shell
153
154 cd tests/topotests/bgp_l3vpn_to_bgp_vrf
155 ./test_bgp_l3vpn_to_bgp_vrf.py
156
157 For further options, refer to pytest documentation.
158
159 Test will set exit code which can be used with ``git bisect``.
160
161 For the simulated topology, see the description in the python file.
162
163 If you need to clear the mininet setup between tests (if it isn't cleanly
164 shutdown), then use the ``mn -c`` command to clean up the environment.
165
166 StdErr log from daemos after exit
167 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
168
169 To enable the reporting of any messages seen on StdErr after the daemons exit,
170 the following env variable can be set::
171
172 export TOPOTESTS_CHECK_STDERR=Yes
173
174 (The value doesn't matter at this time. The check is whether the env
175 variable exists or not.) There is no pass/fail on this reporting; the
176 Output will be reported to the console.
177
178 Collect Memory Leak Information
179 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
180
181 FRR processes can report unfreed memory allocations upon exit. To
182 enable the reporting of memory leaks, define an environment variable
183 ``TOPOTESTS_CHECK_MEMLEAK`` with the file prefix, i.e.::
184
185 export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
186
187 This will enable the check and output to console and the writing of
188 the information to files with the given prefix (followed by testname),
189 ie :file:`/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case
190 of a memory leak.
191
192 Running Topotests with AddressSanitizer
193 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
194
195 Topotests can be run with AddressSanitizer. It requires GCC 4.8 or newer.
196 (Ubuntu 16.04 as suggested here is fine with GCC 5 as default). For more
197 information on AddressSanitizer, see
198 https://github.com/google/sanitizers/wiki/AddressSanitizer.
199
200 The checks are done automatically in the library call of ``checkRouterRunning``
201 (ie at beginning of tests when there is a check for all daemons running). No
202 changes or extra configuration for topotests is required beside compiling the
203 suite with AddressSanitizer enabled.
204
205 If a daemon crashed, then the errorlog is checked for AddressSanitizer output.
206 If found, then this is added with context (calling test) to
207 :file:`/tmp/AddressSanitizer.txt` in Markdown compatible format.
208
209 Compiling for GCC AddressSanitizer requires to use ``gcc`` as a linker as well
210 (instead of ``ld``). Here is a suggest way to compile frr with AddressSanitizer
211 for ``master`` branch:
212
213 .. code:: shell
214
215 git clone https://github.com/FRRouting/frr.git
216 cd frr
217 ./bootstrap.sh
218 ./configure \
219 --enable-address-sanitizer \
220 --prefix=/usr/lib/frr --sysconfdir=/etc/frr \
221 --localstatedir=/var/run/frr \
222 --sbindir=/usr/lib/frr --bindir=/usr/lib/frr \
223 --enable-exampledir=/usr/lib/frr/examples \
224 --with-moduledir=/usr/lib/frr/modules \
225 --enable-multipath=0 --enable-rtadv \
226 --enable-tcp-zebra --enable-fpm --enable-pimd \
227 --enable-sharpd
228 make
229 sudo make install
230 # Create symlink for vtysh, so topotest finds it in /usr/lib/frr
231 sudo ln -s /usr/lib/frr/vtysh /usr/bin/
232
233 and create ``frr`` user and ``frrvty`` group as shown above.
234
235 .. _topotests_docker:
236
237 Running Tests with Docker
238 -------------------------
239
240 There is a Docker image which allows to run topotests.
241
242 Quickstart
243 ^^^^^^^^^^
244
245 If you have Docker installed, you can run the topotests in Docker. The easiest
246 way to do this, is to use the make targets from this repository.
247
248 Your current user needs to have access to the Docker daemon. Alternatively you
249 can run these commands as root.
250
251 .. code:: console
252
253 make topotests
254
255 This command will pull the most recent topotests image from Dockerhub, compile
256 FRR inside of it, and run the topotests.
257
258 Advanced Usage
259 ^^^^^^^^^^^^^^
260
261 Internally, the topotests make target uses a shell script to pull the image and
262 spawn the Docker container.
263
264 There are several environment variables which can be used to modify the
265 behavior of the script, these can be listed by calling it with ``-h``:
266
267 .. code:: console
268
269 ./tests/topotests/docker/frr-topotests.sh -h
270
271 For example, a volume is used to cache build artifacts between multiple runs of
272 the image. If you need to force a complete recompile, you can set
273 ``TOPOTEST_CLEAN``:
274
275 .. code:: console
276
277 TOPOTEST_CLEAN=1 ./tests/topotests/docker/frr-topotests.sh
278
279 By default, ``frr-topotests.sh`` will build frr and run pytest. If you append
280 arguments and the first one starts with ``/`` or ``./``, they will replace the
281 call to pytest. If the appended arguments do not match this patttern, they will
282 be provided to pytest as arguments. So, to run a specific test with more
283 verbose logging:
284
285 .. code:: console
286
287 ./tests/topotests/docker/frr-topotests.sh -vv -s all-protocol-startup/test_all_protocol_startup.py
288
289 And to compile FRR but drop into a shell instead of running pytest:
290
291 .. code:: console
292
293 ./tests/topotests/docker/frr-topotests.sh /bin/bash
294
295 Development
296 ^^^^^^^^^^^
297
298 The Docker image just includes all the components to run the topotests, but not
299 the topotests themselves. So if you just want to write tests and don't want to
300 make changes to the environment provided by the Docker image. You don't need to
301 build your own Docker image if you do not want to.
302
303 When developing new tests, there is one caveat though: The startup script of
304 the container will run a ``git-clean`` on its copy of the FRR tree to avoid any
305 pollution of the container with build artefacts from the host. This will also
306 result in your newly written tests being unavailable in the container unless at
307 least added to the index with ``git-add``.
308
309 If you do want to test changes to the Docker image, you can locally build the
310 image and run the tests without pulling from the registry using the following
311 commands:
312
313 .. code:: console
314
315 make topotests-build
316 TOPOTEST_PULL=0 make topotests
317
318
319 .. _topotests-guidelines:
320
321 Guidelines
322 ----------
323
324 Executing Tests
325 ^^^^^^^^^^^^^^^
326
327 To run the whole suite of tests the following commands must be executed at the
328 top level directory of topotest:
329
330 .. code:: shell
331
332 $ # Change to the top level directory of topotests.
333 $ cd path/to/topotests
334 $ # Tests must be run as root, since Mininet requires it.
335 $ sudo pytest
336
337 In order to run a specific test, you can use the following command:
338
339 .. code:: shell
340
341 $ # running a specific topology
342 $ sudo pytest ospf-topo1/
343 $ # or inside the test folder
344 $ cd ospf-topo1
345 $ sudo pytest # to run all tests inside the directory
346 $ sudo pytest test_ospf_topo1.py # to run a specific test
347 $ # or outside the test folder
348 $ cd ..
349 $ sudo pytest ospf-topo1/test_ospf_topo1.py # to run a specific one
350
351 The output of the tested daemons will be available at the temporary folder of
352 your machine:
353
354 .. code:: shell
355
356 $ ls /tmp/topotest/ospf-topo1.test_ospf-topo1/r1
357 ...
358 zebra.err # zebra stderr output
359 zebra.log # zebra log file
360 zebra.out # zebra stdout output
361 ...
362
363 You can also run memory leak tests to get reports:
364
365 .. code:: shell
366
367 $ # Set the environment variable to apply to a specific test...
368 $ sudo env TOPOTESTS_CHECK_MEMLEAK="/tmp/memleak_report_" pytest ospf-topo1/test_ospf_topo1.py
369 $ # ...or apply to all tests adding this line to the configuration file
370 $ echo 'memleak_path = /tmp/memleak_report_' >> pytest.ini
371 $ # You can also use your editor
372 $ $EDITOR pytest.ini
373 $ # After running tests you should see your files:
374 $ ls /tmp/memleak_report_*
375 memleak_report_test_ospf_topo1.txt
376
377 Writing a New Test
378 ^^^^^^^^^^^^^^^^^^
379
380 This section will guide you in all recommended steps to produce a standard
381 topology test.
382
383 This is the recommended test writing routine:
384
385 - Write a topology (Graphviz recommended)
386 - Obtain configuration files
387 - Write the test itself
388 - Format the new code using `black <https://github.com/psf/black>`_
389 - Create a Pull Request
390
391 .. Note::
392
393 BGP tests MUST use generous convergence timeouts - you must ensure
394 that any test involving BGP uses a convergence timeout of at least
395 130 seconds.
396
397 Topotest File Hierarchy
398 """""""""""""""""""""""
399
400 Before starting to write any tests one must know the file hierarchy. The
401 repository hierarchy looks like this:
402
403 .. code:: shell
404
405 $ cd path/to/topotest
406 $ find ./*
407 ...
408 ./README.md # repository read me
409 ./GUIDELINES.md # this file
410 ./conftest.py # test hooks - pytest related functions
411 ./example-test # example test folder
412 ./example-test/__init__.py # python package marker - must always exist.
413 ./example-test/test_template.jpg # generated topology picture - see next section
414 ./example-test/test_template.dot # Graphviz dot file
415 ./example-test/test_template.py # the topology plus the test
416 ...
417 ./ospf-topo1 # the ospf topology test
418 ./ospf-topo1/r1 # router 1 configuration files
419 ./ospf-topo1/r1/zebra.conf # zebra configuration file
420 ./ospf-topo1/r1/ospfd.conf # ospf configuration file
421 ./ospf-topo1/r1/ospfroute.txt # 'show ip ospf' output reference file
422 # removed other for shortness sake
423 ...
424 ./lib # shared test/topology functions
425 ./lib/topogen.py # topogen implementation
426 ./lib/topotest.py # topotest implementation
427
428 Guidelines for creating/editing topotest:
429
430 - New topologies that don't fit the existing directories should create its own
431 - Always remember to add the ``__init__.py`` to new folders, this makes auto
432 complete engines and pylint happy
433 - Router (Quagga/FRR) specific code should go on topotest.py
434 - Generic/repeated router actions should have an abstraction in
435 topogen.TopoRouter.
436 - Generic/repeated non-router code should go to topotest.py
437 - pytest related code should go to conftest.py (e.g. specialized asserts)
438
439 Defining the Topology
440 """""""""""""""""""""
441
442 The first step to write a new test is to define the topology. This step can be
443 done in many ways, but the recommended is to use Graphviz to generate a drawing
444 of the topology. It allows us to see the topology graphically and to see the
445 names of equipment, links and addresses.
446
447 Here is an example of Graphviz dot file that generates the template topology
448 :file:`tests/topotests/example-test/test_template.dot` (the inlined code might
449 get outdated, please see the linked file)::
450
451 graph template {
452 label="template";
453
454 # Routers
455 r1 [
456 shape=doubleoctagon,
457 label="r1",
458 fillcolor="#f08080",
459 style=filled,
460 ];
461 r2 [
462 shape=doubleoctagon,
463 label="r2",
464 fillcolor="#f08080",
465 style=filled,
466 ];
467
468 # Switches
469 s1 [
470 shape=oval,
471 label="s1\n192.168.0.0/24",
472 fillcolor="#d0e0d0",
473 style=filled,
474 ];
475 s2 [
476 shape=oval,
477 label="s2\n192.168.1.0/24",
478 fillcolor="#d0e0d0",
479 style=filled,
480 ];
481
482 # Connections
483 r1 -- s1 [label="eth0\n.1"];
484
485 r1 -- s2 [label="eth1\n.100"];
486 r2 -- s2 [label="eth0\n.1"];
487 }
488
489 Here is the produced graph:
490
491 .. graphviz::
492
493 graph template {
494 label="template";
495
496 # Routers
497 r1 [
498 shape=doubleoctagon,
499 label="r1",
500 fillcolor="#f08080",
501 style=filled,
502 ];
503 r2 [
504 shape=doubleoctagon,
505 label="r2",
506 fillcolor="#f08080",
507 style=filled,
508 ];
509
510 # Switches
511 s1 [
512 shape=oval,
513 label="s1\n192.168.0.0/24",
514 fillcolor="#d0e0d0",
515 style=filled,
516 ];
517 s2 [
518 shape=oval,
519 label="s2\n192.168.1.0/24",
520 fillcolor="#d0e0d0",
521 style=filled,
522 ];
523
524 # Connections
525 r1 -- s1 [label="eth0\n.1"];
526
527 r1 -- s2 [label="eth1\n.100"];
528 r2 -- s2 [label="eth0\n.1"];
529 }
530
531 Generating / Obtaining Configuration Files
532 """"""""""""""""""""""""""""""""""""""""""
533
534 In order to get the configuration files or command output for each router, we
535 need to run the topology and execute commands in ``vtysh``. The quickest way to
536 achieve that is writing the topology building code and running the topology.
537
538 To bootstrap your test topology, do the following steps:
539
540 - Copy the template test
541
542 .. code:: shell
543
544 $ mkdir new-topo/
545 $ touch new-topo/__init__.py
546 $ cp example-test/test_template.py new-topo/test_new_topo.py
547
548 - Modify the template according to your dot file
549
550 Here is the template topology described in the previous section in python code:
551
552 .. code:: py
553
554 class TemplateTopo(Topo):
555 "Test topology builder"
556 def build(self, *_args, **_opts):
557 "Build function"
558 tgen = get_topogen(self)
559
560 # Create 2 routers
561 for routern in range(1, 3):
562 tgen.add_router('r{}'.format(routern))
563
564 # Create a switch with just one router connected to it to simulate a
565 # empty network.
566 switch = tgen.add_switch('s1')
567 switch.add_link(tgen.gears['r1'])
568
569 # Create a connection between r1 and r2
570 switch = tgen.add_switch('s2')
571 switch.add_link(tgen.gears['r1'])
572 switch.add_link(tgen.gears['r2'])
573
574 - Run the topology
575
576 Topogen allows us to run the topology without running any tests, you can do
577 that using the following example commands:
578
579 .. code:: shell
580
581 $ # Running your bootstraped topology
582 $ sudo pytest -s --topology-only new-topo/test_new_topo.py
583 $ # Running the test_template.py topology
584 $ sudo pytest -s --topology-only example-test/test_template.py
585 $ # Running the ospf_topo1.py topology
586 $ sudo pytest -s --topology-only ospf-topo1/test_ospf_topo1.py
587
588 Parameters explanation:
589
590 .. program:: pytest
591
592 .. option:: -s
593
594 Actives input/output capture. This is required by mininet in order to show
595 the interactive shell.
596
597 .. option:: --topology-only
598
599 Don't run any tests, just build the topology.
600
601 After executing the commands above, you should get the following terminal
602 output:
603
604 .. code:: shell
605
606 === test session starts ===
607 platform linux2 -- Python 2.7.12, pytest-3.1.2, py-1.4.34, pluggy-0.4.0
608 rootdir: /media/sf_src/topotests, inifile: pytest.ini
609 collected 3 items
610
611 ospf-topo1/test_ospf_topo1.py *** Starting controller
612
613 *** Starting 6 switches
614 switch1 switch2 switch3 switch4 switch5 switch6 ...
615 r2: frr zebra started
616 r2: frr ospfd started
617 r3: frr zebra started
618 r3: frr ospfd started
619 r1: frr zebra started
620 r1: frr ospfd started
621 r4: frr zebra started
622 r4: frr ospfd started
623 *** Starting CLI:
624 mininet>
625
626 The last line shows us that we are now using the Mininet CLI (Command Line
627 Interface), from here you can call your router ``vtysh`` or even bash.
628
629 Here are some commands example:
630
631 .. code:: shell
632
633 mininet> r1 ping 10.0.3.1
634 PING 10.0.3.1 (10.0.3.1) 56(84) bytes of data.
635 64 bytes from 10.0.3.1: icmp_seq=1 ttl=64 time=0.576 ms
636 64 bytes from 10.0.3.1: icmp_seq=2 ttl=64 time=0.083 ms
637 64 bytes from 10.0.3.1: icmp_seq=3 ttl=64 time=0.088 ms
638 ^C
639 --- 10.0.3.1 ping statistics ---
640 3 packets transmitted, 3 received, 0% packet loss, time 1998ms
641 rtt min/avg/max/mdev = 0.083/0.249/0.576/0.231 ms
642
643
644
645 mininet> r1 ping 10.0.3.3
646 PING 10.0.3.3 (10.0.3.3) 56(84) bytes of data.
647 64 bytes from 10.0.3.3: icmp_seq=1 ttl=64 time=2.87 ms
648 64 bytes from 10.0.3.3: icmp_seq=2 ttl=64 time=0.080 ms
649 64 bytes from 10.0.3.3: icmp_seq=3 ttl=64 time=0.091 ms
650 ^C
651 --- 10.0.3.3 ping statistics ---
652 3 packets transmitted, 3 received, 0% packet loss, time 2003ms
653 rtt min/avg/max/mdev = 0.080/1.014/2.872/1.313 ms
654
655
656
657 mininet> r3 vtysh
658
659 Hello, this is FRRouting (version 3.1-devrzalamena-build).
660 Copyright 1996-2005 Kunihiro Ishiguro, et al.
661
662 frr-1# show running-config
663 Building configuration...
664
665 Current configuration:
666 !
667 frr version 3.1-devrzalamena-build
668 frr defaults traditional
669 hostname r3
670 no service integrated-vtysh-config
671 !
672 log file zebra.log
673 !
674 log file ospfd.log
675 !
676 interface r3-eth0
677 ip address 10.0.3.1/24
678 !
679 interface r3-eth1
680 ip address 10.0.10.1/24
681 !
682 interface r3-eth2
683 ip address 172.16.0.2/24
684 !
685 router ospf
686 ospf router-id 10.0.255.3
687 redistribute kernel
688 redistribute connected
689 redistribute static
690 network 10.0.3.0/24 area 0
691 network 10.0.10.0/24 area 0
692 network 172.16.0.0/24 area 1
693 !
694 line vty
695 !
696 end
697 frr-1#
698
699 After you successfully configured your topology, you can obtain the
700 configuration files (per-daemon) using the following commands:
701
702 .. code:: shell
703
704 mininet> r3 vtysh -d ospfd
705
706 Hello, this is FRRouting (version 3.1-devrzalamena-build).
707 Copyright 1996-2005 Kunihiro Ishiguro, et al.
708
709 frr-1# show running-config
710 Building configuration...
711
712 Current configuration:
713 !
714 frr version 3.1-devrzalamena-build
715 frr defaults traditional
716 no service integrated-vtysh-config
717 !
718 log file ospfd.log
719 !
720 router ospf
721 ospf router-id 10.0.255.3
722 redistribute kernel
723 redistribute connected
724 redistribute static
725 network 10.0.3.0/24 area 0
726 network 10.0.10.0/24 area 0
727 network 172.16.0.0/24 area 1
728 !
729 line vty
730 !
731 end
732 frr-1#
733
734 Writing Tests
735 """""""""""""
736
737 Test topologies should always be bootstrapped from
738 :file:`tests/topotests/example-test/test_template.py` because it contains
739 important boilerplate code that can't be avoided, like:
740
741 - imports: os, sys, pytest, topotest/topogen and mininet topology class
742 - The global variable CWD (Current Working directory): which is most likely
743 going to be used to reference the routers configuration file location
744
745 Example:
746
747 .. code:: py
748
749 # For all registered routers, load the zebra configuration file
750 for rname, router in router_list.items():
751 router.load_config(
752 TopoRouter.RD_ZEBRA,
753 os.path.join(CWD, '{}/zebra.conf'.format(rname))
754 )
755 # os.path.join() joins the CWD string with arguments adding the necessary
756 # slashes ('/'). Arguments must not begin with '/'.
757
758 - The topology class that inherits from Mininet Topo class:
759
760 .. code:: py
761
762 class TemplateTopo(Topo):
763 def build(self, *_args, **_opts):
764 tgen = get_topogen(self)
765 # topology build code
766
767 - pytest ``setup_module()`` and ``teardown_module()`` to start the topology
768
769 .. code:: py
770
771 def setup_module(_m):
772 tgen = Topogen(TemplateTopo)
773 tgen.start_topology('debug')
774
775 def teardown_module(_m):
776 tgen = get_topogen()
777 tgen.stop_topology()
778
779 - ``__main__`` initialization code (to support running the script directly)
780
781 .. code:: py
782
783 if __name__ == '__main__':
784 sys.exit(pytest.main(["-s"]))
785
786 Requirements:
787
788 - Test code should always be declared inside functions that begin with the
789 ``test_`` prefix. Functions beginning with different prefixes will not be run
790 by pytest.
791 - Configuration files and long output commands should go into separated files
792 inside folders named after the equipment.
793 - Tests must be able to run without any interaction. To make sure your test
794 conforms with this, run it without the :option:`-s` parameter.
795 - Use `black <https://github.com/psf/black>`_ code formatter before creating
796 a pull request. This ensures we have a unified code style.
797 - Mark test modules with pytest markers depending on the daemons used during the
798 tests (s. Markers)
799
800 Tips:
801
802 - Keep results in stack variables, so people inspecting code with ``pdb`` can
803 easily print their values.
804
805 Don't do this:
806
807 .. code:: py
808
809 assert foobar(router1, router2)
810
811 Do this instead:
812
813 .. code:: py
814
815 result = foobar(router1, router2)
816 assert result
817
818 - Use ``assert`` messages to indicate where the test failed.
819
820 Example:
821
822 .. code:: py
823
824 for router in router_list:
825 # ...
826 assert condition, 'Router "{}" condition failed'.format(router.name)
827
828 Debugging Execution
829 ^^^^^^^^^^^^^^^^^^^
830
831 The most effective ways to inspect topology tests are:
832
833 - Run pytest with ``--pdb`` option. This option will cause a pdb shell to
834 appear when an assertion fails
835
836 Example: ``pytest -s --pdb ospf-topo1/test_ospf_topo1.py``
837
838 - Set a breakpoint in the test code with ``pdb``
839
840 Example:
841
842 .. code:: py
843
844 # Add the pdb import at the beginning of the file
845 import pdb
846 # ...
847
848 # Add a breakpoint where you think the problem is
849 def test_bla():
850 # ...
851 pdb.set_trace()
852 # ...
853
854 The `Python Debugger <https://docs.python.org/2.7/library/pdb.html>`__ (pdb)
855 shell allows us to run many useful operations like:
856
857 - Setting breaking point on file/function/conditions (e.g. ``break``,
858 ``condition``)
859 - Inspecting variables (e.g. ``p`` (print), ``pp`` (pretty print))
860 - Running python code
861
862 .. tip::
863
864 The TopoGear (equipment abstraction class) implements the ``__str__`` method
865 that allows the user to inspect equipment information.
866
867 Example of pdb usage:
868
869 .. code:: shell
870
871 > /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(121)test_ospf_convergence()
872 -> for rnum in range(1, 5):
873 (Pdb) help
874 Documented commands (type help <topic>):
875 ========================================
876 EOF bt cont enable jump pp run unt
877 a c continue exit l q s until
878 alias cl d h list quit step up
879 args clear debug help n r tbreak w
880 b commands disable ignore next restart u whatis
881 break condition down j p return unalias where
882
883 Miscellaneous help topics:
884 ==========================
885 exec pdb
886
887 Undocumented commands:
888 ======================
889 retval rv
890
891 (Pdb) list
892 116 title2="Expected output")
893 117
894 118 def test_ospf_convergence():
895 119 "Test OSPF daemon convergence"
896 120 pdb.set_trace()
897 121 -> for rnum in range(1, 5):
898 122 router = 'r{}'.format(rnum)
899 123
900 124 # Load expected results from the command
901 125 reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
902 126 expected = open(reffile).read()
903 (Pdb) step
904 > /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(122)test_ospf_convergence()
905 -> router = 'r{}'.format(rnum)
906 (Pdb) step
907 > /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(125)test_ospf_convergence()
908 -> reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
909 (Pdb) print rnum
910 1
911 (Pdb) print router
912 r1
913 (Pdb) tgen = get_topogen()
914 (Pdb) pp tgen.gears[router]
915 <lib.topogen.TopoRouter object at 0x7f74e06c9850>
916 (Pdb) pp str(tgen.gears[router])
917 'TopoGear<name="r1",links=["r1-eth0"<->"s1-eth0","r1-eth1"<->"s3-eth0"]> TopoRouter<>'
918 (Pdb) l 125
919 120 pdb.set_trace()
920 121 for rnum in range(1, 5):
921 122 router = 'r{}'.format(rnum)
922 123
923 124 # Load expected results from the command
924 125 -> reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
925 126 expected = open(reffile).read()
926 127
927 128 # Run test function until we get an result. Wait at most 60 seconds.
928 129 test_func = partial(compare_show_ip_ospf, router, expected)
929 130 result, diff = topotest.run_and_expect(test_func, '',
930 (Pdb) router1 = tgen.gears[router]
931 (Pdb) router1.vtysh_cmd('show ip ospf route')
932 '============ 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'
933 (Pdb) tgen.mininet_cli()
934 *** Starting CLI:
935 mininet>
936
937 To enable more debug messages in other Topogen subsystems (like Mininet), more
938 logging messages can be displayed by modifying the test configuration file
939 ``pytest.ini``:
940
941 .. code:: ini
942
943 [topogen]
944 # Change the default verbosity line from 'info'...
945 #verbosity = info
946 # ...to 'debug'
947 verbosity = debug
948
949 Instructions for use, write or debug topologies can be found in :ref:`topotests-guidelines`.
950 To learn/remember common code snippets see :ref:`topotests-snippets`.
951
952 Before creating a new topology, make sure that there isn't one already that
953 does what you need. If nothing is similar, then you may create a new topology,
954 preferably, using the newest template
955 (:file:`tests/topotests/example-test/test_template.py`).
956
957 .. include:: topotests-markers.rst
958
959 .. include:: topotests-snippets.rst
960
961 License
962 -------
963
964 All the configs and scripts are licensed under a ISC-style license. See Python
965 scripts for details.