From: Rafael Zalamena Date: Thu, 9 Nov 2017 19:35:01 +0000 (-0200) Subject: doc: update README and add code snippets X-Git-Tag: frr-7.1-dev~151^2~197 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=28a9b4f56e565d711088c85edb72b04fb2828ca6;p=matthieu%2Ffrr.git doc: update README and add code snippets Add some pointers in the README documentation that might help users get ready to use topotest. --- diff --git a/tests/topotests/README.md b/tests/topotests/README.md index 3176c9ef93..823f5d72bf 100644 --- a/tests/topotests/README.md +++ b/tests/topotests/README.md @@ -1,5 +1,17 @@ # FRRouting Topology Tests with Mininet +## Guidelines + +Instructions for use, write or debug topologies can be found in the +[guidelines](GUIDELINES.md). To learn/remember common code snippets see +[here](SNIPPETS.md). + +Before creating a new topology, make sure that there isn't one already +that does what you need. If nothing is similar, then you may create a +new topology, preferably, using the newest +[template](example-test/test_template.py). + + ## Installation of Mininet for running tests Only tested with Ubuntu 16.04 (which uses Mininet 2.2.0) diff --git a/tests/topotests/SNIPPETS.md b/tests/topotests/SNIPPETS.md new file mode 100644 index 0000000000..6c16c44af6 --- /dev/null +++ b/tests/topotests/SNIPPETS.md @@ -0,0 +1,275 @@ +# Snippets + +This document will describe common snippets of code that are frequently +needed to perform some test checks. + + +## Checking for router / test failures + +The following check uses the topogen API to check for software failure +(e.g. zebra died) and/or for errors manually set by `Topogen.set_error()`. + +```py +# Get the topology reference +tgen = get_topogen() + +# Check for errors in the topology +if tgen.routers_have_failure(): + # Skip the test with the topology errors as reason + pytest.skip(tgen.errors) +``` + + +## Checking FRR routers version + +This code snippet is usually run after the topology setup to make sure +all routers instantiated in the topology have the correct software +version. + +```py +# Get the topology reference +tgen = get_topogen() + +# Get the router list +router_list = tgen.routers() + +# Run the check for all routers +for router in router_list.values(): + if router.has_version('<', '3'): + # Set topology error, so the next tests are skipped + tgen.set_error('unsupported version') +``` + +A sample of this snippet in a test can be found +[here](ldp-vpls-topo1/test_ldp_vpls_topo1.py). + + +## Interacting with equipments + +You might want to interact with the topology equipments during the tests +and there are different ways to do so. + +Notes: + +1. +> When using the Topogen API, all the equipments code derive from +> `Topogear` ([lib/topogen.py](lib/topogen.py)). If you feel brave you +> can look by yourself how the abstractions that will be mentioned here +> works. + +2. +> When not using the `Topogen` API there is only one way to interact +> with the equipments, which is by calling the `mininet` API functions +> directly to spawn commands. + + +### Interacting with the Linux sandbox + +*Without `Topogen`* + +```py +global net +output = net['r1'].cmd('echo "foobar"') +print 'output is: {}'.format(output) +``` + +--- + +*With `Topogen`* + +```py +tgen = get_topogen() +output = tgen.gears['r1'].run('echo "foobar"') +print 'output is: {}'.format(output) +``` + + +### Interacting with VTYSH + +*Without `Topogen`* + +```py +global net +output = net['r1'].cmd('vtysh "show ip route" 2>/dev/null') +print 'output is: {}'.format(output) +``` + +--- + +*With `Topogen`* + +```py +tgen = get_topogen() +output = tgen.gears['r1'].vtysh_cmd("show ip route") +print 'output is: {}'.format(output) +``` + +`Topogen` also supports sending multiple lines of command: + +```py +tgen = get_topogen() +output = tgen.gears['r1'].vtysh_cmd(""" +configure terminal +router bgp 10 + bgp router-id 10.0.255.1 + neighbor 1.2.3.4 remote-as 10 + ! +router bgp 11 + bgp router-id 10.0.255.2 + ! +""") +print 'output is: {}'.format(output) +``` + +You might also want to run multiple commands and get only the commands +that failed: + +```py +tgen = get_topogen() +output = tgen.gears['r1'].vtysh_multicmd(""" +configure terminal +router bgp 10 + bgp router-id 10.0.255.1 + neighbor 1.2.3.4 remote-as 10 + ! +router bgp 11 + bgp router-id 10.0.255.2 + ! +""", pretty_output=false) +print 'output is: {}'.format(output) +``` + +Translating vtysh JSON output into Python structures: +```py +tgen = get_topogen() +json_output = tgen.gears['r1'].vtysh_cmd("show ip route json", isjson=True) +output = json.dumps(json_output, indent=4) +print 'output is: {}'.format(output) + +# You can also access the data structure as normal. For example: +# protocol = json_output['1.1.1.1/32']['protocol'] +# assert protocol == "ospf", "wrong protocol" +``` + +*NOTE:* `vtysh_(multi)cmd` is only available for router type of +equipments. + + +### Invoking `mininet` CLI + +*Without `Topogen`* + +```py +CLI(net) +``` + +--- + +*With `Topogen`* +```py +tgen = get_topogen() +tgen.mininet_cli() +``` + + +## Reading files + +Loading a normal text file content in the current directory: + +```py +# If you are using Topogen +# CURDIR = CWD +# +# Otherwise find the directory manually: +CURDIR = os.path.dirname(os.path.realpath(__file__)) + +file_name = '{}/r1/show_ip_route.txt'.format(CURDIR) +file_content = open(file_name).read() +``` + +Loading JSON from a file: + +```py +import json + +file_name = '{}/r1/show_ip_route.json'.format(CURDIR) +file_content = json.loads(open(file_name).read()) +``` + + +## Comparing JSON output + +After obtaining JSON output formated with Python data structures, you +may use it to assert a minimalist schema: + +```py +tgen = get_topogen() +json_output = tgen.gears['r1'].vtysh_cmd("show ip route json", isjson=True) + +expect = { + '1.1.1.1/32': { + 'protocol': 'ospf' + } +} + +assertmsg = "route 1.1.1.1/32 was not learned through OSPF" +assert json_cmp(json_output, expect) is None, assertmsg +``` + +`json_cmp` function description (it might be outdated, you can find the +latest description in the source code at [lib/topotest.py](lib/topotest.py)): + +```text + JSON compare function. Receives two parameters: + * `d1`: json value + * `d2`: json subset which we expect + + Returns `None` when all keys that `d1` has matches `d2`, + otherwise a string containing what failed. + + Note: key absence can be tested by adding a key with value `None`. +``` + + +## Pausing execution + +Preferably, choose the `sleep` function that `topotest` provides, as it +prints a notice during the test execution to help debug topology test +execution time. + +```py +# Using the topotest sleep +from lib import topotest + +topotest.sleep(10, 'waiting 10 seconds for bla') +# or just tell it the time: +# topotest.sleep(10) +# It will print 'Sleeping for 10 seconds'. + +# Or you can also use the Python sleep, but it won't show anything +from time import sleep +sleep(5) +``` + + +## `ip route` Linux command as JSON + +`topotest` has two helpers implemented that parses the output of +`ip route` commands to JSON. It might simplify your comparison needs by +only needing to provide a Python dictionary. + +```py +from lib import topotest + +tgen = get_topogen() +routes = topotest.ip4_route(tgen.gears['r1']) +expected = { + '10.0.1.0/24': {}, + '10.0.2.0/24': { + 'dev': 'r1-eth0' + } +} + +assertmsg = "failed to find 10.0.1.0/24 and/or 10.0.2.0/24" +assert json_cmp(routes, expected) is None, assertmsg +```