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