9 On top of current topotests framework following enhancements are done:
12 * Creating the topology and assigning IPs to router' interfaces dynamically.
13 It is achieved by using json file, in which user specify the number of
14 routers, links to each router, interfaces for the routers and protocol
15 configurations for all routers.
17 * Creating the configurations dynamically. It is achieved by using
18 :file:`/usr/lib/frr/frr-reload.py` utility, which takes running configuration
19 and the newly created configuration for any particular router and creates a
20 delta file(diff file) and loads it to router.
23 Logging of test case executions
24 -------------------------------
26 * The user can enable logging of testcases execution messages into log file by
27 adding ``frrtest_log_dir = /tmp/topotests/`` in :file:`pytest.ini`.
28 * Router's current configuration can be displyed on console or sent to logs by
29 adding ``show_router_config = True`` in :file:`pytest.ini`.
31 Log file name will be displayed when we start execution:
33 .. code-block:: console
35 root@test:# python ./test_topo_json_single_link.py
37 Logs will be sent to logfile:
38 /tmp/topotests/test_topo_json_single_link_11:57:01.353797
40 Note: directory "/tmp/topotests/" is created by topotests by default, making
41 use of same directory to save execution logs.
49 This section will guide you in all recommended steps to produce a standard
52 This is the recommended test writing routine:
54 * Create a json file , which will have routers and protocol configurations
55 * Create topology from json
56 * Create configuration from json
58 * Create a Pull Request
64 Before starting to write any tests one must know the file hierarchy. The
65 repository hierarchy looks like this:
67 .. code-block:: console
69 $ cd path/to/topotests
72 ./example-topojson-test # the basic example test topology-1
73 ./example-topojson-test/test_example_topojson.json # input json file, having
74 topology, interfaces, bgp and other configuration
75 ./example-topojson-test/test_example_topojson.py # test script to write and
78 ./lib # shared test/topology functions
79 ./lib/topojson.py # library to create topology and configurations dynamically
81 ./lib/common_config.py # library to create protocol's common configurations ex-
82 static_routes, prefix_lists, route_maps etc.
83 ./lib/bgp.py # library to create only bgp configurations
85 Defining the Topology and initial configuration in JSON file
86 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
88 The first step to write a new test is to define the topology and initial
89 configuration. User has to define topology and initial configuration in JSON
90 file. Here is an example of JSON file::
92 BGP neihghborship with single phy-link, sample JSON file:
94 "ipv4base": "192.168.0.0",
98 "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
99 "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
103 "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
104 "r2": {"ipv4": "auto", "ipv6": "auto"},
105 "r3": {"ipv4": "auto", "ipv6": "auto"}
131 "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
132 "r1": {"ipv4": "auto", "ipv6": "auto"},
133 "r3": {"ipv4": "auto", "ipv6": "auto"}
142 "redist_type": "static"
165 BGP neighboship with loopback interface, sample JSON file::
168 "ipv4base": "192.168.0.0",
170 "ipv6base": "fd00::",
172 "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
173 "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
177 "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback",
178 "add_static_route":"yes"},
179 "r2": {"ipv4": "auto", "ipv6": "auto"}
201 "network": "1.0.2.17/32",
202 "next_hop": "192.168.0.1
208 "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback",
209 "add_static_route":"yes"},
210 "r1": {"ipv4": "auto", "ipv6": "auto"},
211 "r3": {"ipv4": "auto", "ipv6": "auto"}
220 "redist_type": "static"
245 "network": "192.0.20.1/32",
247 "admin_distance": 100,
248 "next_hop": "192.168.0.1",
255 BGP neighborship with Multiple phy-links, sample JSON file::
258 "ipv4base": "192.168.0.0",
260 "ipv6base": "fd00::",
262 "link_ip_start": {"ipv4": "192.168.0.0", "v4mask": 30, "ipv6": "fd00::", "v6mask": 64},
263 "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
267 "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
268 "r2-link1": {"ipv4": "auto", "ipv6": "auto"},
269 "r2-link2": {"ipv4": "auto", "ipv6": "auto"}
290 "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
291 "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
292 "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
293 "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
294 "r3-link2": {"ipv4": "auto", "ipv6": "auto"}
303 "redist_type": "static"
329 Mandatory keywords/options in JSON:
331 * ``ipv4base`` : base ipv4 address to generate ips, ex - 192.168.0.0
332 * ``ipv4mask`` : mask for ipv4 address, ex - 30
333 * ``ipv6base`` : base ipv6 address to generate ips, ex - fd00:
334 * ``ipv6mask`` : mask for ipv6 address, ex - 64
335 * ``link_ip_start`` : physical interface base ipv4 and ipv6 address
336 * ``lo_prefix`` : loopback interface base ipv4 and ipv6 address
337 * ``routers`` : user can add number of routers as per topology, router's name
338 can be any logical name, ex- r1 or a0.
339 * ``r1`` : name of the router
340 * ``lo`` : loopback interface dict, ipv4 and/or ipv6 addresses generated automatically
341 * ``type`` : type of interface, to identify loopback interface
342 * ``links`` : physical interfaces dict, ipv4 and/or ipv6 addresses generated
344 * ``r2-link1`` : it will be used when routers have multiple links. 'r2' is router
345 name, 'link' is any logical name, '1' is to identify link number,
346 router name and link must be seperated by hyphen (``-``), ex- a0-peer1
348 Optional keywords/options in JSON:
350 * ``bgp`` : bgp configuration
351 * ``local_as`` : Local AS number
352 * ``unicast`` : All SAFI configuration
353 * ``neighbor``: All neighbor details
354 * ``dest_link`` : Destination link to which router will connect
355 * ``router_id`` : bgp router-id
356 * ``source_link`` : if user wants to establish bgp neighborship with loopback
357 interface, add ``source_link``: ``lo``
358 * ``keepalivetimer`` : Keep alive timer for BGP neighbor
359 * ``holddowntimer`` : Hold down timer for BGP neighbor
360 * ``static_routes`` : create static routes for routers
361 * ``redistribute`` : redistribute static and/or connected routes
362 * ``prefix_lists`` : create Prefix-lists for routers
364 Building topology and configurations
365 """"""""""""""""""""""""""""""""""""
367 Topology and initial configuration will be created in setup_module(). Following
370 class TemplateTopo(Topo):
371 def build(self, *_args, **_opts):
373 tgen = get_topogen(self)
375 # Building topology from json file
376 build_topo_from_json(tgen, topo)
378 def setup_module(mod):
379 tgen = Topogen(TemplateTopo, mod.__name__)
381 # Starting topology, create tmp files which are loaded to routers
382 # to start deamons and then start routers
385 # Creating configuration from JSON
386 build_config_from_json(tgen, topo)
388 def teardown_module(mod):
391 # Stop toplogy and Remove tmp files
395 * Note: Topology will be created in setup module but routers will not be
396 started until we load zebra.conf and bgpd.conf to routers. For all routers
397 dirs will be created in /tmp/topotests/<test_folder_name>/<router_name>
398 zebra.conf and bgpd.conf empty files will be created and laoded to routers.
399 All folder and files are deleted in teardown module..
401 Creating configuration files
402 """"""""""""""""""""""""""""
404 Router's configuration would be saved in config file frr_json.conf. Common
405 configurations are like, static routes, prefixlists and route maps etc configs,
406 these configs can be used by any other protocols as it is.
407 BGP config will be specific to BGP protocol testing.
409 * JSON file is passed to API build_config_from_json(), which looks for
410 configuration tags in JSON file.
411 * If tag is found in JSON, configuration is created as per input and written
412 to file frr_json.conf
413 * Once JSON parsing is over, frr_json.conf is loaded onto respective router.
414 Config loading is done using 'vtysh -f <file>'. Initial config at this point
415 is also saved frr_json_initial.conf. This file can be used to reset
416 configuration on router, during the course of execution.
417 * Reset of configuration is done using frr "reload.py" utility, which
418 calculates the difference between router's running config and user's config
419 and loads delta file to router. API used - reset_config_on_router()
424 Test topologies should always be bootstrapped from the
425 example-test/test_example.py, because it contains important boilerplate code
426 that can't be avoided, like:
428 imports: os, sys, pytest, topotest/topogen and mininet topology class
430 The global variable CWD (Current Working directory): which is most likely going
431 to be used to reference the routers configuration file location
436 * The topology class that inherits from Mininet Topo class;
438 .. code-block:: python
440 class TemplateTopo(Topo):
441 def build(self, *_args, **_opts):
442 tgen = get_topogen(self)
443 # topology build code
446 * pytest setup_module() and teardown_module() to start the topology:
448 .. code-block:: python
450 def setup_module(_m):
451 tgen = Topogen(TemplateTopo)
453 # Starting topology, create tmp files which are loaded to routers
454 # to start deamons and then start routers
455 start_topology(tgen, CWD)
457 def teardown_module(_m):
460 # Stop toplogy and Remove tmp files
461 stop_topology(tgen, CWD)
464 * ``__main__`` initialization code (to support running the script directly)
466 .. code-block:: python
468 if **name** == '\ **main**\ ':
469 sys.exit(pytest.main(["-s"]))