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