mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-08-05 02:46:37 +00:00
doc: update README and add code snippets
Add some pointers in the README documentation that might help users get ready to use topotest.
This commit is contained in:
parent
222ea88b2a
commit
28a9b4f56e
@ -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)
|
||||
|
||||
|
275
tests/topotests/SNIPPETS.md
Normal file
275
tests/topotests/SNIPPETS.md
Normal file
@ -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
|
||||
```
|
Loading…
Reference in New Issue
Block a user