mirror of
https://git.proxmox.com/git/mirror_frr
synced 2025-07-29 19:37:26 +00:00
commit
f2ce6a57a8
2
tests/pytest.ini
Normal file
2
tests/pytest.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
norecursedirs = topotests
|
4
tests/topotests/.gitignore
vendored
Normal file
4
tests/topotests/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.cache
|
||||
__pycache__
|
||||
*.pyc
|
||||
.pytest_cache
|
571
tests/topotests/GUIDELINES.md
Normal file
571
tests/topotests/GUIDELINES.md
Normal file
@ -0,0 +1,571 @@
|
||||
# Guidelines
|
||||
|
||||
This document describes how to use the topotests testing framework.
|
||||
|
||||
|
||||
## Executing Tests
|
||||
|
||||
To run the whole suite of tests the following commands must be executed at the
|
||||
top level directory of topotest:
|
||||
|
||||
```shell
|
||||
$ # Change to the top level directory of topotests.
|
||||
$ cd path/to/topotests
|
||||
$ # Tests must be run as root, since Mininet requires it.
|
||||
$ sudo pytest
|
||||
```
|
||||
|
||||
In order to run a specific test, you can use the following command:
|
||||
|
||||
```shell
|
||||
$ # running a specific topology
|
||||
$ sudo pytest ospf-topo1/
|
||||
$ # or inside the test folder
|
||||
$ cd ospf-topo1
|
||||
$ sudo pytest # to run all tests inside the directory
|
||||
$ sudo pytest test_ospf_topo1.py # to run a specific test
|
||||
$ # or outside the test folder
|
||||
$ cd ..
|
||||
$ sudo pytest ospf-topo1/test_ospf_topo1.py # to run a specific one
|
||||
```
|
||||
|
||||
The output of the tested daemons will be available at the temporary folder of
|
||||
your machine:
|
||||
|
||||
```shell
|
||||
$ ls /tmp/topotest/ospf-topo1.test_ospf-topo1/r1
|
||||
...
|
||||
zebra.err # zebra stderr output
|
||||
zebra.log # zebra log file
|
||||
zebra.out # zebra stdout output
|
||||
...
|
||||
```
|
||||
|
||||
You can also run memory leak tests to get reports:
|
||||
|
||||
```shell
|
||||
$ # Set the environment variable to apply to a specific test...
|
||||
$ sudo env TOPOTESTS_CHECK_MEMLEAK="/tmp/memleak_report_" pytest ospf-topo1/test_ospf_topo1.py
|
||||
$ # ...or apply to all tests adding this line to the configuration file
|
||||
$ echo 'memleak_path = /tmp/memleak_report_' >> pytest.ini
|
||||
$ # You can also use your editor
|
||||
$ $EDITOR pytest.ini
|
||||
$ # After running tests you should see your files:
|
||||
$ ls /tmp/memleak_report_*
|
||||
memleak_report_test_ospf_topo1.txt
|
||||
```
|
||||
|
||||
|
||||
## Writing a New Test
|
||||
|
||||
This section will guide you in all recommended steps to produce a standard
|
||||
topology test.
|
||||
|
||||
This is the recommended test writing routine:
|
||||
|
||||
* Write a topology (Graphviz recommended)
|
||||
* Obtain configuration files
|
||||
* Write the test itself
|
||||
* Create a Pull Request
|
||||
|
||||
|
||||
### Topotest File Hierarchy
|
||||
|
||||
Before starting to write any tests one must know the file hierarchy. The
|
||||
repository hierarchy looks like this:
|
||||
|
||||
```shell
|
||||
$ cd path/to/topotest
|
||||
$ find ./*
|
||||
...
|
||||
./README.md # repository read me
|
||||
./GUIDELINES.md # this file
|
||||
./conftest.py # test hooks - pytest related functions
|
||||
./example-test # example test folder
|
||||
./example-test/__init__.py # python package marker - must always exist.
|
||||
./example-test/test_template.jpg # generated topology picture - see next section
|
||||
./example-test/test_template.dot # Graphviz dot file
|
||||
./example-test/test_template.py # the topology plus the test
|
||||
...
|
||||
./ospf-topo1 # the ospf topology test
|
||||
./ospf-topo1/r1 # router 1 configuration files
|
||||
./ospf-topo1/r1/zebra.conf # zebra configuration file
|
||||
./ospf-topo1/r1/ospfd.conf # ospf configuration file
|
||||
./ospf-topo1/r1/ospfroute.txt # 'show ip ospf' output reference file
|
||||
# removed other for shortness sake
|
||||
...
|
||||
./lib # shared test/topology functions
|
||||
./lib/topogen.py # topogen implementation
|
||||
./lib/topotest.py # topotest implementation
|
||||
```
|
||||
|
||||
Guidelines for creating/editing topotest:
|
||||
|
||||
* New topologies that don't fit the existing directories should create its own
|
||||
* Always remember to add the `__init__.py` to new folders, this makes auto
|
||||
complete engines and pylint happy
|
||||
* Router (Quagga/FRR) specific code should go on topotest.py
|
||||
* Generic/repeated router actions should have an abstraction in
|
||||
topogen.TopoRouter.
|
||||
* Generic/repeated non-router code should go to topotest.py
|
||||
* pytest related code should go to conftest.py (e.g. specialized asserts)
|
||||
|
||||
|
||||
### Defining the Topology
|
||||
|
||||
The first step to write a new test is to define the topology. This step can be
|
||||
done in many ways, but the recommended is to use Graphviz to generate a drawing
|
||||
of the Topology. It allows us to see the topology graphically and to see the
|
||||
names of equipments, links and addresses.
|
||||
|
||||
Here is an example of Graphviz dot file that generates the
|
||||
[template topology](example-test/test_template.dot) (the inlined code might get
|
||||
outdated, please see the linked file):
|
||||
|
||||
```dot
|
||||
graph template {
|
||||
label="template";
|
||||
|
||||
# Routers
|
||||
r1 [
|
||||
shape=doubleoctagon,
|
||||
label="r1",
|
||||
fillcolor="#f08080",
|
||||
style=filled,
|
||||
];
|
||||
r2 [
|
||||
shape=doubleoctagon,
|
||||
label="r2",
|
||||
fillcolor="#f08080",
|
||||
style=filled,
|
||||
];
|
||||
|
||||
# Switches
|
||||
s1 [
|
||||
shape=oval,
|
||||
label="s1\n192.168.0.0/24",
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
s2 [
|
||||
shape=oval,
|
||||
label="s2\n192.168.1.0/24",
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
|
||||
# Connections
|
||||
r1 -- s1 [label="eth0\n.1"];
|
||||
|
||||
r1 -- s2 [label="eth1\n.100"];
|
||||
r2 -- s2 [label="eth0\n.1"];
|
||||
}
|
||||
```
|
||||
|
||||
Here is the produced graph:
|
||||
|
||||

|
||||
|
||||
|
||||
### Generating / Obtaining Configuration Files
|
||||
|
||||
In order to get the configuration files or command output for each router, we
|
||||
need to run the topology and execute commands in vtysh. The quickest way to
|
||||
achieve that is writing the topology building code and running the topology.
|
||||
|
||||
To bootstrap your test topology, do the following steps:
|
||||
|
||||
* Copy the template test
|
||||
|
||||
```shell
|
||||
$ mkdir new-topo/
|
||||
$ touch new-topo/__init__.py
|
||||
$ cp example-test/test_template.py new-topo/test_new_topo.py
|
||||
```
|
||||
|
||||
* Modify the template according to your dot file
|
||||
|
||||
Here is the template topology described in the previous section in python code:
|
||||
|
||||
```py
|
||||
class TemplateTopo(Topo):
|
||||
"Test topology builder"
|
||||
def build(self, *_args, **_opts):
|
||||
"Build function"
|
||||
tgen = get_topogen(self)
|
||||
|
||||
# Create 2 routers
|
||||
for routern in range(1, 3):
|
||||
tgen.add_router('r{}'.format(routern))
|
||||
|
||||
# Create a switch with just one router connected to it to simulate a
|
||||
# empty network.
|
||||
switch = tgen.add_switch('s1')
|
||||
switch.add_link(tgen.gears['r1'])
|
||||
|
||||
# Create a connection between r1 and r2
|
||||
switch = tgen.add_switch('s2')
|
||||
switch.add_link(tgen.gears['r1'])
|
||||
switch.add_link(tgen.gears['r2'])
|
||||
```
|
||||
|
||||
* Run the topology
|
||||
|
||||
Topogen allows us to run the topology without running any tests, you can do that
|
||||
using the following example commands:
|
||||
|
||||
```shell
|
||||
$ # Running your bootstraped topology
|
||||
$ sudo pytest -s --topology-only new-topo/test_new_topo.py
|
||||
$ # Running the test_template.py topology
|
||||
$ sudo pytest -s --topology-only example-test/test_template.py
|
||||
$ # Running the ospf_topo1.py topology
|
||||
$ sudo pytest -s --topology-only ospf-topo1/test_ospf_topo1.py
|
||||
```
|
||||
|
||||
Parameters explanation:
|
||||
|
||||
* `-s`: actives input/output capture. This is required by mininet in order to show
|
||||
the interactive shell.
|
||||
* `--topology-only`: don't run any tests, just build the topology.
|
||||
|
||||
After executing the commands above, you should get the following terminal
|
||||
output:
|
||||
|
||||
```shell
|
||||
=== test session starts ===
|
||||
platform linux2 -- Python 2.7.12, pytest-3.1.2, py-1.4.34, pluggy-0.4.0
|
||||
rootdir: /media/sf_src/topotests, inifile: pytest.ini
|
||||
collected 3 items
|
||||
|
||||
ospf-topo1/test_ospf_topo1.py *** Starting controller
|
||||
|
||||
*** Starting 6 switches
|
||||
switch1 switch2 switch3 switch4 switch5 switch6 ...
|
||||
r2: frr zebra started
|
||||
r2: frr ospfd started
|
||||
r3: frr zebra started
|
||||
r3: frr ospfd started
|
||||
r1: frr zebra started
|
||||
r1: frr ospfd started
|
||||
r4: frr zebra started
|
||||
r4: frr ospfd started
|
||||
*** Starting CLI:
|
||||
mininet>
|
||||
```
|
||||
|
||||
The last line shows us that we are now using the Mininet CLI (Command Line
|
||||
Interface), from here you can call your router vtysh or even bash.
|
||||
|
||||
Here are some commands example:
|
||||
|
||||
```shell
|
||||
mininet> r1 ping 10.0.3.1
|
||||
PING 10.0.3.1 (10.0.3.1) 56(84) bytes of data.
|
||||
64 bytes from 10.0.3.1: icmp_seq=1 ttl=64 time=0.576 ms
|
||||
64 bytes from 10.0.3.1: icmp_seq=2 ttl=64 time=0.083 ms
|
||||
64 bytes from 10.0.3.1: icmp_seq=3 ttl=64 time=0.088 ms
|
||||
^C
|
||||
--- 10.0.3.1 ping statistics ---
|
||||
3 packets transmitted, 3 received, 0% packet loss, time 1998ms
|
||||
rtt min/avg/max/mdev = 0.083/0.249/0.576/0.231 ms
|
||||
|
||||
|
||||
|
||||
mininet> r1 ping 10.0.3.3
|
||||
PING 10.0.3.3 (10.0.3.3) 56(84) bytes of data.
|
||||
64 bytes from 10.0.3.3: icmp_seq=1 ttl=64 time=2.87 ms
|
||||
64 bytes from 10.0.3.3: icmp_seq=2 ttl=64 time=0.080 ms
|
||||
64 bytes from 10.0.3.3: icmp_seq=3 ttl=64 time=0.091 ms
|
||||
^C
|
||||
--- 10.0.3.3 ping statistics ---
|
||||
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
|
||||
rtt min/avg/max/mdev = 0.080/1.014/2.872/1.313 ms
|
||||
|
||||
|
||||
|
||||
mininet> r3 vtysh
|
||||
|
||||
Hello, this is FRRouting (version 3.1-devrzalamena-build).
|
||||
Copyright 1996-2005 Kunihiro Ishiguro, et al.
|
||||
|
||||
frr-1# show running-config
|
||||
Building configuration...
|
||||
|
||||
Current configuration:
|
||||
!
|
||||
frr version 3.1-devrzalamena-build
|
||||
frr defaults traditional
|
||||
hostname r3
|
||||
no service integrated-vtysh-config
|
||||
!
|
||||
log file zebra.log
|
||||
!
|
||||
log file ospfd.log
|
||||
!
|
||||
interface r3-eth0
|
||||
ip address 10.0.3.1/24
|
||||
!
|
||||
interface r3-eth1
|
||||
ip address 10.0.10.1/24
|
||||
!
|
||||
interface r3-eth2
|
||||
ip address 172.16.0.2/24
|
||||
!
|
||||
router ospf
|
||||
ospf router-id 10.0.255.3
|
||||
redistribute kernel
|
||||
redistribute connected
|
||||
redistribute static
|
||||
network 10.0.3.0/24 area 0
|
||||
network 10.0.10.0/24 area 0
|
||||
network 172.16.0.0/24 area 1
|
||||
!
|
||||
line vty
|
||||
!
|
||||
end
|
||||
frr-1#
|
||||
```
|
||||
|
||||
After you successfully configured your topology, you can obtain the
|
||||
configuration files (per-daemon) using the following commands:
|
||||
|
||||
```shell
|
||||
mininet> r3 vtysh -d ospfd
|
||||
|
||||
Hello, this is FRRouting (version 3.1-devrzalamena-build).
|
||||
Copyright 1996-2005 Kunihiro Ishiguro, et al.
|
||||
|
||||
frr-1# show running-config
|
||||
Building configuration...
|
||||
|
||||
Current configuration:
|
||||
!
|
||||
frr version 3.1-devrzalamena-build
|
||||
frr defaults traditional
|
||||
no service integrated-vtysh-config
|
||||
!
|
||||
log file ospfd.log
|
||||
!
|
||||
router ospf
|
||||
ospf router-id 10.0.255.3
|
||||
redistribute kernel
|
||||
redistribute connected
|
||||
redistribute static
|
||||
network 10.0.3.0/24 area 0
|
||||
network 10.0.10.0/24 area 0
|
||||
network 172.16.0.0/24 area 1
|
||||
!
|
||||
line vty
|
||||
!
|
||||
end
|
||||
frr-1#
|
||||
```
|
||||
|
||||
|
||||
### Writing Tests
|
||||
|
||||
Test topologies should always be bootstrapped from the
|
||||
[example-test/test_template.py](example-test/test_template.py),
|
||||
because it contains important boilerplate code that can't be avoided, like:
|
||||
|
||||
* imports: os, sys, pytest, topotest/topogen and mininet topology class
|
||||
* The global variable CWD (Current Working directory): which is most likely
|
||||
going to be used to reference the routers configuration file location
|
||||
|
||||
Example:
|
||||
|
||||
```py
|
||||
# For all registered routers, load the zebra configuration file
|
||||
for rname, router in router_list.iteritems():
|
||||
router.load_config(
|
||||
TopoRouter.RD_ZEBRA,
|
||||
os.path.join(CWD, '{}/zebra.conf'.format(rname))
|
||||
)
|
||||
# os.path.join() joins the CWD string with arguments adding the necessary
|
||||
# slashes ('/'). Arguments must not begin with '/'.
|
||||
```
|
||||
|
||||
* The topology class that inherits from Mininet Topo class
|
||||
|
||||
```py
|
||||
class TemplateTopo(Topo):
|
||||
def build(self, *_args, **_opts):
|
||||
tgen = get_topogen(self)
|
||||
# topology build code
|
||||
```
|
||||
|
||||
* pytest `setup_module()` and `teardown_module()` to start the topology
|
||||
|
||||
```py
|
||||
def setup_module(_m):
|
||||
tgen = Topogen(TemplateTopo)
|
||||
tgen.start_topology('debug')
|
||||
|
||||
def teardown_module(_m):
|
||||
tgen = get_topogen()
|
||||
tgen.stop_topology()
|
||||
```
|
||||
|
||||
* `__main__` initialization code (to support running the script directly)
|
||||
|
||||
```py
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main(["-s"]))
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
* Test code should always be declared inside functions that begin with the
|
||||
`test_` prefix. Functions beginning with different prefixes will not be run by
|
||||
pytest.
|
||||
* Configuration files and long output commands should go into separated files
|
||||
inside folders named after the equipment.
|
||||
* Tests must be able to run without any interaction. To make sure your test
|
||||
conforms with this, run it without the `-s` parameter.
|
||||
|
||||
Tips:
|
||||
|
||||
* Keep results in stack variables, so people inspecting code with `pdb` can
|
||||
easily print their values.
|
||||
|
||||
Don't do this:
|
||||
|
||||
```py
|
||||
assert foobar(router1, router2)
|
||||
```
|
||||
|
||||
Do this instead:
|
||||
|
||||
```py
|
||||
result = foobar(router1, router2)
|
||||
assert result
|
||||
```
|
||||
|
||||
* Use `assert` messages to indicate where the test failed.
|
||||
|
||||
Example:
|
||||
|
||||
```py
|
||||
for router in router_list:
|
||||
# ...
|
||||
assert condition, 'Router "{}" condition failed'.format(router.name)
|
||||
```
|
||||
|
||||
|
||||
### Debugging Execution
|
||||
|
||||
The most effective ways to inspect topology tests are:
|
||||
|
||||
* Run pytest with `--pdb` option. This option will cause a pdb shell to appear
|
||||
when an assertion fails
|
||||
|
||||
Example: `pytest -s --pdb ospf-topo1/test_ospf_topo1.py`
|
||||
|
||||
* Set a breakpoint in the test code with `pdb`
|
||||
|
||||
Example:
|
||||
|
||||
```py
|
||||
# Add the pdb import at the beginning of the file
|
||||
import pdb
|
||||
# ...
|
||||
|
||||
# Add a breakpoint where you think the problem is
|
||||
def test_bla():
|
||||
# ...
|
||||
pdb.set_trace()
|
||||
# ...
|
||||
```
|
||||
|
||||
The [Python Debugger](https://docs.python.org/2.7/library/pdb.html) (pdb) shell
|
||||
allows us to run many useful operations like:
|
||||
|
||||
* Setting breaking point on file/function/conditions (e.g. `break`, `condition`)
|
||||
* Inspecting variables (e.g. `p` (print), `pp` (pretty print))
|
||||
* Running python code
|
||||
|
||||
TIP: The TopoGear (equipment abstraction class) implements the `__str__` method
|
||||
that allows the user to inspect equipment information.
|
||||
|
||||
Example of pdb usage:
|
||||
|
||||
```shell
|
||||
> /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(121)test_ospf_convergence()
|
||||
-> for rnum in range(1, 5):
|
||||
(Pdb) help
|
||||
Documented commands (type help <topic>):
|
||||
========================================
|
||||
EOF bt cont enable jump pp run unt
|
||||
a c continue exit l q s until
|
||||
alias cl d h list quit step up
|
||||
args clear debug help n r tbreak w
|
||||
b commands disable ignore next restart u whatis
|
||||
break condition down j p return unalias where
|
||||
|
||||
Miscellaneous help topics:
|
||||
==========================
|
||||
exec pdb
|
||||
|
||||
Undocumented commands:
|
||||
======================
|
||||
retval rv
|
||||
|
||||
(Pdb) list
|
||||
116 title2="Expected output")
|
||||
117
|
||||
118 def test_ospf_convergence():
|
||||
119 "Test OSPF daemon convergence"
|
||||
120 pdb.set_trace()
|
||||
121 -> for rnum in range(1, 5):
|
||||
122 router = 'r{}'.format(rnum)
|
||||
123
|
||||
124 # Load expected results from the command
|
||||
125 reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
|
||||
126 expected = open(reffile).read()
|
||||
(Pdb) step
|
||||
> /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(122)test_ospf_convergence()
|
||||
-> router = 'r{}'.format(rnum)
|
||||
(Pdb) step
|
||||
> /media/sf_src/topotests/ospf-topo1/test_ospf_topo1.py(125)test_ospf_convergence()
|
||||
-> reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
|
||||
(Pdb) print rnum
|
||||
1
|
||||
(Pdb) print router
|
||||
r1
|
||||
(Pdb) tgen = get_topogen()
|
||||
(Pdb) pp tgen.gears[router]
|
||||
<lib.topogen.TopoRouter object at 0x7f74e06c9850>
|
||||
(Pdb) pp str(tgen.gears[router])
|
||||
'TopoGear<name="r1",links=["r1-eth0"<->"s1-eth0","r1-eth1"<->"s3-eth0"]> TopoRouter<>'
|
||||
(Pdb) l 125
|
||||
120 pdb.set_trace()
|
||||
121 for rnum in range(1, 5):
|
||||
122 router = 'r{}'.format(rnum)
|
||||
123
|
||||
124 # Load expected results from the command
|
||||
125 -> reffile = os.path.join(CWD, '{}/ospfroute.txt'.format(router))
|
||||
126 expected = open(reffile).read()
|
||||
127
|
||||
128 # Run test function until we get an result. Wait at most 60 seconds.
|
||||
129 test_func = partial(compare_show_ip_ospf, router, expected)
|
||||
130 result, diff = topotest.run_and_expect(test_func, '',
|
||||
(Pdb) router1 = tgen.gears[router]
|
||||
(Pdb) router1.vtysh_cmd('show ip ospf route')
|
||||
'============ 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'
|
||||
(Pdb) tgen.mininet_cli()
|
||||
*** Starting CLI:
|
||||
mininet>
|
||||
```
|
||||
|
||||
To enable more debug messages in other Topogen subsystems (like Mininet), more
|
||||
logging messages can be displayed by modifying the test configuration file
|
||||
`pytest.ini`:
|
||||
|
||||
```ini
|
||||
[topogen]
|
||||
# Change the default verbosity line from 'info'...
|
||||
#verbosity = info
|
||||
# ...to 'debug'
|
||||
verbosity = debug
|
||||
```
|
195
tests/topotests/README.md
Normal file
195
tests/topotests/README.md
Normal file
@ -0,0 +1,195 @@
|
||||
# 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 and Ubuntu 18.04 (which uses Mininet 2.2.x)
|
||||
|
||||
Instructions are the same for all setups (ie ExaBGP is only used for BGP
|
||||
tests)
|
||||
|
||||
### Installing Mininet Infrastructure:
|
||||
|
||||
1. apt-get install mininet
|
||||
2. apt-get install python-pip
|
||||
3. apt-get install iproute
|
||||
4. pip install ipaddr
|
||||
5. pip install pytest
|
||||
6. pip install exabgp==3.4.17
|
||||
(Newer 4.0 version of exabgp is not yet supported)
|
||||
7. useradd -d /var/run/exabgp/ -s /bin/false exabgp
|
||||
|
||||
### Enable Coredumps
|
||||
Optional, will give better output
|
||||
|
||||
1. apt-get install gdb
|
||||
2. disable apport (which move core files)
|
||||
|
||||
Set `enabled=0` in `/etc/default/apport`
|
||||
|
||||
3. Update security limits
|
||||
|
||||
Add/change `/etc/security/limits.conf` to
|
||||
|
||||
#<domain> <type> <item> <value>
|
||||
* soft core unlimited
|
||||
root soft core unlimited
|
||||
* hard core unlimited
|
||||
root hard core unlimited
|
||||
|
||||
4. reboot (for options to take effect)
|
||||
|
||||
## FRRouting (FRR) Installation
|
||||
FRR needs to be installed separatly. It is assume to be configured
|
||||
like the standard Ubuntu Packages:
|
||||
|
||||
- Binaries in /usr/lib/frr
|
||||
- State Directory /var/run/frr
|
||||
- Running under user frr, group frr
|
||||
- vtygroup: frrvty
|
||||
- config directory: /etc/frr
|
||||
- For FRR Packages, install the dbg package as well for coredump decoding
|
||||
|
||||
No FRR config needs to be done and no FRR daemons should be run ahead
|
||||
of the test. They are all started as part of the test
|
||||
|
||||
#### Manual FRRouting (FRR) build
|
||||
|
||||
If you prefer to manually build FRR, then use the following suggested config:
|
||||
|
||||
./configure \
|
||||
--prefix=/usr \
|
||||
--localstatedir=/var/run/frr \
|
||||
--sbindir=/usr/lib/frr \
|
||||
--sysconfdir=/etc/frr \
|
||||
--enable-vtysh \
|
||||
--enable-pimd \
|
||||
--enable-multipath=64 \
|
||||
--enable-user=frr \
|
||||
--enable-group=frr \
|
||||
--enable-vty-group=frrvty \
|
||||
--with-pkg-extra-version=-my-manual-build
|
||||
|
||||
And create frr User and frrvty group as follows:
|
||||
|
||||
addgroup --system --gid 92 frr
|
||||
addgroup --system --gid 85 frrvty
|
||||
adduser --system --ingroup frr --home /var/run/frr/ \
|
||||
--gecos "FRRouting suite" --shell /bin/false frr
|
||||
usermod -G frrvty frr
|
||||
|
||||
## Executing Tests
|
||||
|
||||
#### Execute all tests with output to console
|
||||
|
||||
py.test -s -v --tb=no
|
||||
|
||||
All test_* scripts in subdirectories are detected and executed (unless
|
||||
disabled in `pytest.ini` file)
|
||||
|
||||
`--tb=no` disables the python traceback which might be irrelevant unless the
|
||||
test script itself is debugged
|
||||
|
||||
#### Execute single test
|
||||
|
||||
cd test_to_be_run
|
||||
./test_to_be_run.py
|
||||
|
||||
For further options, refer to pytest documentation
|
||||
|
||||
Test will set exit code which can be used with `git bisect`
|
||||
|
||||
For the simulated topology, see the description in the python file
|
||||
|
||||
If you need to clear the mininet setup between tests (if it isn't cleanly
|
||||
shutdown), then use the `mn -c` command to clean up the environment
|
||||
|
||||
#### (Optional) StdErr log from daemos after exit
|
||||
|
||||
To enable the reporting of any messages seen on StdErr after the
|
||||
daemons exit, the following env variable can be set.
|
||||
|
||||
export TOPOTESTS_CHECK_STDERR=Yes
|
||||
|
||||
(The value doesn't matter at this time. The check is if the env variable
|
||||
exists or not)
|
||||
There is no pass/fail on this reporting. The Output will be reported to
|
||||
the console
|
||||
|
||||
export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
|
||||
|
||||
This will enable the check and output to console and the writing of
|
||||
the information to files with the given prefix (followed by testname),
|
||||
ie `/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a
|
||||
memory leak.
|
||||
|
||||
#### (Optional) Collect Memory Leak Information
|
||||
|
||||
FreeRangeRouting processes have the capabilities to report remaining memory
|
||||
allocations upon exit. To enable the reporting of the memory, define an
|
||||
enviroment variable `TOPOTESTS_CHECK_MEMLEAK` with the file prefix, ie
|
||||
|
||||
export TOPOTESTS_CHECK_MEMLEAK="/home/mydir/memleak_"
|
||||
|
||||
This will enable the check and output to console and the writing of
|
||||
the information to files with the given prefix (followed by testname),
|
||||
ie `/home/mydir/memcheck_test_bgp_multiview_topo1.txt` in case of a
|
||||
memory leak.
|
||||
|
||||
#### (Optional) Run topotests with GCC AddressSanitizer enabled
|
||||
|
||||
Topotests can be run with the GCC AddressSanitizer. It requires GCC 4.8 or
|
||||
newer. (Ubuntu 16.04 as suggested here is fine with GCC 5 as default)
|
||||
For more information on AddressSanitizer, see
|
||||
https://github.com/google/sanitizers/wiki/AddressSanitizer
|
||||
|
||||
The checks are done automatically in the library call of `checkRouterRunning`
|
||||
(ie at beginning of tests when there is a check for all daemons running).
|
||||
No changes or extra configuration for topotests is required beside compiling
|
||||
the suite with AddressSanitizer enabled.
|
||||
|
||||
If a daemon crashed, then the errorlog is checked for AddressSanitizer
|
||||
output. If found, then this is added with context (calling test) to
|
||||
`/tmp/AddressSanitizer.txt` in markdown compatible format.
|
||||
|
||||
Compiling for GCC AddressSanitizer requires to use gcc as a linker as well
|
||||
(instead of ld). Here is a suggest way to compile frr with AddressSanitizer
|
||||
for `stable/3.0` branch:
|
||||
|
||||
git clone https://github.com/FRRouting/frr.git
|
||||
cd frr
|
||||
git checkout stable/3.0
|
||||
./bootstrap.sh
|
||||
export CC=gcc
|
||||
export CFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer"
|
||||
export LD=gcc
|
||||
export LDFLAGS="-g -fsanitize=address -ldl"
|
||||
./configure --enable-shared=no \
|
||||
--prefix=/usr/lib/frr --sysconfdir=/etc/frr \
|
||||
--localstatedir=/var/run/frr \
|
||||
--sbindir=/usr/lib/frr --bindir=/usr/lib/frr \
|
||||
--enable-exampledir=/usr/lib/frr/examples \
|
||||
--with-moduledir=/usr/lib/frr/modules \
|
||||
--enable-multipath=0 --enable-rtadv \
|
||||
--enable-tcp-zebra --enable-fpm --enable-pimd
|
||||
make
|
||||
sudo make install
|
||||
# Create symlink for vtysh, so topotest finds it in /usr/lib/frr
|
||||
sudo ln -s /usr/lib/frr/vtysh /usr/bin/
|
||||
|
||||
and create `frr` user and `frrvty` group as shown above
|
||||
|
||||
## License
|
||||
|
||||
All the configs and scripts are licensed under a ISC-style license. See
|
||||
Python scripts for details.
|
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
|
||||
```
|
47
tests/topotests/all-protocol-startup/r1/bgpd.conf
Normal file
47
tests/topotests/all-protocol-startup/r1/bgpd.conf
Normal file
@ -0,0 +1,47 @@
|
||||
log file bgpd.log
|
||||
!
|
||||
!
|
||||
router bgp 100
|
||||
bgp router-id 192.168.0.1
|
||||
bgp log-neighbor-changes
|
||||
neighbor 192.168.7.10 remote-as 100
|
||||
neighbor 192.168.7.20 remote-as 200
|
||||
neighbor fc00:0:0:8::1000 remote-as 100
|
||||
neighbor fc00:0:0:8::2000 remote-as 200
|
||||
!
|
||||
address-family ipv4 unicast
|
||||
network 192.168.0.0/24
|
||||
neighbor 192.168.7.10 route-map bgp-map in
|
||||
neighbor 192.168.7.10 filter-list bgp-filter-v4 out
|
||||
neighbor 192.168.7.20 route-map bgp-map in
|
||||
neighbor 192.168.7.20 filter-list bgp-filter-v4 out
|
||||
exit-address-family
|
||||
!
|
||||
address-family ipv6 unicast
|
||||
network fc00::/64
|
||||
neighbor fc00:0:0:8::1000 activate
|
||||
neighbor fc00:0:0:8::1000 route-map bgp-map in
|
||||
neighbor fc00:0:0:8::1000 filter-list bgp-filter-v6 out
|
||||
neighbor fc00:0:0:8::2000 activate
|
||||
neighbor fc00:0:0:8::2000 route-map bgp-map in
|
||||
neighbor fc00:0:0:8::2000 filter-list bgp-filter-v6 out
|
||||
exit-address-family
|
||||
!
|
||||
!
|
||||
ip prefix-list bgp-filter-v4 description dummy-test-prefix-list
|
||||
ip prefix-list bgp-filter-v4 seq 5 permit 192.168.0.0/24
|
||||
!
|
||||
ipv6 prefix-list bgp-filter-v4 seq 5 permit fc00::/64
|
||||
ipv6 prefix-list bgp-filter-v6 description dummy-test-prefix-list-v6
|
||||
!
|
||||
route-map bgp-map permit 10
|
||||
set community 100:100 additive
|
||||
set local-preference 100
|
||||
!
|
||||
route-map bgp-map permit 20
|
||||
set metric 10
|
||||
set local-preference 200
|
||||
!
|
||||
line vty
|
||||
!
|
||||
|
21
tests/topotests/all-protocol-startup/r1/isisd.conf
Normal file
21
tests/topotests/all-protocol-startup/r1/isisd.conf
Normal file
@ -0,0 +1,21 @@
|
||||
log file isisd.log
|
||||
!
|
||||
debug isis events
|
||||
!
|
||||
!
|
||||
interface r1-eth5
|
||||
ip router isis test
|
||||
isis circuit-type level-1
|
||||
!
|
||||
interface r1-eth6
|
||||
ipv6 router isis test
|
||||
isis circuit-type level-2-only
|
||||
!
|
||||
!
|
||||
router isis test
|
||||
net 00.0001.00b0.64bc.43a0.00
|
||||
metric-style wide
|
||||
log-adjacency-changes
|
||||
!
|
||||
line vty
|
||||
!
|
25
tests/topotests/all-protocol-startup/r1/ldpd.conf
Normal file
25
tests/topotests/all-protocol-startup/r1/ldpd.conf
Normal file
@ -0,0 +1,25 @@
|
||||
log file ldpd.log
|
||||
!
|
||||
debug mpls ldp event
|
||||
debug mpls ldp zebra
|
||||
!
|
||||
!
|
||||
mpls ldp
|
||||
router-id 192.168.0.1
|
||||
!
|
||||
address-family ipv4
|
||||
discovery transport-address 192.168.9.1
|
||||
!
|
||||
interface r1-eth9
|
||||
!
|
||||
!
|
||||
address-family ipv6
|
||||
discovery transport-address fc00:0:0:9::1
|
||||
!
|
||||
interface r1-eth9
|
||||
!
|
||||
!
|
||||
!
|
||||
!
|
||||
line vty
|
||||
!
|
16
tests/topotests/all-protocol-startup/r1/ospf6d.conf
Normal file
16
tests/topotests/all-protocol-startup/r1/ospf6d.conf
Normal file
@ -0,0 +1,16 @@
|
||||
log file ospf6d.log
|
||||
!
|
||||
debug ospf6 lsa unknown
|
||||
debug ospf6 zebra
|
||||
debug ospf6 interface
|
||||
debug ospf6 neighbor
|
||||
!
|
||||
interface r1-eth4
|
||||
!
|
||||
router ospf6
|
||||
ospf6 router-id 192.168.0.1
|
||||
log-adjacency-changes
|
||||
interface r1-eth4 area 0.0.0.0
|
||||
!
|
||||
line vty
|
||||
!
|
16
tests/topotests/all-protocol-startup/r1/ospf6d.conf-pre-v4
Normal file
16
tests/topotests/all-protocol-startup/r1/ospf6d.conf-pre-v4
Normal file
@ -0,0 +1,16 @@
|
||||
log file ospf6d.log
|
||||
!
|
||||
debug ospf6 lsa unknown
|
||||
debug ospf6 zebra
|
||||
debug ospf6 interface
|
||||
debug ospf6 neighbor
|
||||
!
|
||||
interface r1-eth4
|
||||
!
|
||||
router ospf6
|
||||
router-id 192.168.0.1
|
||||
log-adjacency-changes
|
||||
interface r1-eth4 area 0.0.0.0
|
||||
!
|
||||
line vty
|
||||
!
|
13
tests/topotests/all-protocol-startup/r1/ospfd.conf
Normal file
13
tests/topotests/all-protocol-startup/r1/ospfd.conf
Normal file
@ -0,0 +1,13 @@
|
||||
log file ospfd.log
|
||||
!
|
||||
debug ospf event
|
||||
debug ospf zebra
|
||||
!
|
||||
router ospf
|
||||
ospf router-id 192.168.0.1
|
||||
log-adjacency-changes
|
||||
network 192.168.0.0/24 area 0.0.0.0
|
||||
network 192.168.3.0/24 area 0.0.0.0
|
||||
!
|
||||
line vty
|
||||
!
|
15
tests/topotests/all-protocol-startup/r1/rip_status.ref
Normal file
15
tests/topotests/all-protocol-startup/r1/rip_status.ref
Normal file
@ -0,0 +1,15 @@
|
||||
Routing Protocol is "rip"
|
||||
Sending updates every 30 seconds with +/-50%, next due in XX seconds
|
||||
Timeout after 180 seconds, garbage collect after 120 seconds
|
||||
Outgoing update filter list for all interface is not set
|
||||
Incoming update filter list for all interface is not set
|
||||
Default redistribution metric is 1
|
||||
Redistributing:
|
||||
Default version control: send version 2, receive version 2
|
||||
Interface Send Recv Key-chain
|
||||
r1-eth1 2 2
|
||||
Routing for Networks:
|
||||
192.168.1.0/26
|
||||
Routing Information Sources:
|
||||
Gateway BadPackets BadRoutes Distance Last Update
|
||||
Distance: (default is 120)
|
12
tests/topotests/all-protocol-startup/r1/ripd.conf
Normal file
12
tests/topotests/all-protocol-startup/r1/ripd.conf
Normal file
@ -0,0 +1,12 @@
|
||||
log file ripd.log
|
||||
!
|
||||
debug rip events
|
||||
debug rip zebra
|
||||
!
|
||||
router rip
|
||||
version 2
|
||||
network 192.168.1.0/26
|
||||
!
|
||||
line vty
|
||||
!
|
||||
|
14
tests/topotests/all-protocol-startup/r1/ripng_status.ref
Normal file
14
tests/topotests/all-protocol-startup/r1/ripng_status.ref
Normal file
@ -0,0 +1,14 @@
|
||||
Routing Protocol is "RIPng"
|
||||
Sending updates every 30 seconds with +/-50%, next due in XX seconds
|
||||
Timeout after 180 seconds, garbage collect after 120 seconds
|
||||
Outgoing update filter list for all interface is not set
|
||||
Incoming update filter list for all interface is not set
|
||||
Default redistribution metric is 1
|
||||
Redistributing:
|
||||
Default version control: send version 1, receive version 1
|
||||
Interface Send Recv
|
||||
r1-eth2 1 1
|
||||
Routing for Networks:
|
||||
fc00:0:0:2::/64
|
||||
Routing Information Sources:
|
||||
Gateway BadPackets BadRoutes Distance Last Update
|
11
tests/topotests/all-protocol-startup/r1/ripngd.conf
Normal file
11
tests/topotests/all-protocol-startup/r1/ripngd.conf
Normal file
@ -0,0 +1,11 @@
|
||||
log file ripngd.log
|
||||
!
|
||||
debug ripng events
|
||||
debug ripng zebra
|
||||
!
|
||||
router ripng
|
||||
network fc00:0:0:2::/64
|
||||
!
|
||||
line vty
|
||||
!
|
||||
|
@ -0,0 +1,8 @@
|
||||
BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
|
||||
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
|
||||
i internal, r RIB-failure, S Stale, R Removed
|
||||
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
|
||||
Origin codes: i - IGP, e - EGP, ? - incomplete
|
||||
|
||||
Network Next Hop Metric LocPrf Weight Path
|
||||
*> 192.168.0.0 0.0.0.0 0 32768 i
|
@ -0,0 +1,8 @@
|
||||
BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
|
||||
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
|
||||
i internal, r RIB-failure, S Stale, R Removed
|
||||
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
|
||||
Origin codes: i - IGP, e - EGP, ? - incomplete
|
||||
|
||||
Network Next Hop Metric LocPrf Weight Path
|
||||
*> 192.168.0.0/24 0.0.0.0 0 32768 i
|
@ -0,0 +1,7 @@
|
||||
BGP table version is 1, local router ID is 192.168.0.1
|
||||
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
|
||||
i internal, r RIB-failure, S Stale, R Removed
|
||||
Origin codes: i - IGP, e - EGP, ? - incomplete
|
||||
|
||||
Network Next Hop Metric LocPrf Weight Path
|
||||
*> 192.168.0.0 0.0.0.0 0 32768 i
|
@ -0,0 +1,8 @@
|
||||
BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
|
||||
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
|
||||
i internal, r RIB-failure, S Stale, R Removed
|
||||
Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
|
||||
Origin codes: i - IGP, e - EGP, ? - incomplete
|
||||
|
||||
Network Next Hop Metric LocPrf Weight Path
|
||||
*> fc00::/64 :: 0 32768 i
|
@ -0,0 +1,7 @@
|
||||
BGP table version is 1, local router ID is 192.168.0.1
|
||||
Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
|
||||
i internal, r RIB-failure, S Stale, R Removed
|
||||
Origin codes: i - IGP, e - EGP, ? - incomplete
|
||||
|
||||
Network Next Hop Metric LocPrf Weight Path
|
||||
*> fc00::/64 :: 0 32768 i
|
@ -0,0 +1,8 @@
|
||||
BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0
|
||||
BGP table version 1
|
||||
RIB entries 1, using XXXX bytes of memory
|
||||
Peers 4, using XXXX KiB of memory
|
||||
|
||||
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
|
||||
fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active
|
||||
fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active
|
@ -0,0 +1,10 @@
|
||||
BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0
|
||||
BGP table version 1
|
||||
RIB entries 1, using XXXX bytes of memory
|
||||
Peers 4, using XXXX KiB of memory
|
||||
|
||||
Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
|
||||
192.168.7.10 4 100 0 0 0 0 0 never Active
|
||||
192.168.7.20 4 200 0 0 0 0 0 never Active
|
||||
fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active
|
||||
fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active
|
@ -0,0 +1,22 @@
|
||||
r1-eth0 is up
|
||||
ifindex 2, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
|
||||
Internet Address 192.168.0.1/24, Broadcast 192.168.0.255, Area 0.0.0.0
|
||||
MTU mismatch detection: enabled
|
||||
Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10
|
||||
Transmit Delay is 1 sec, State DR, Priority 1
|
||||
No backup designated router on this network
|
||||
Multicast group memberships: OSPFAllRouters OSPFDesignatedRouters
|
||||
Timer intervals configured, Hello 10s, Dead 40s, Wait 40s, Retransmit 5
|
||||
Hello due in XX.XXXs
|
||||
Neighbor Count is 0, Adjacent neighbor count is 0
|
||||
r1-eth3 is up
|
||||
ifindex 5, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
|
||||
Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0
|
||||
MTU mismatch detection: enabled
|
||||
Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10
|
||||
Transmit Delay is 1 sec, State DR, Priority 1
|
||||
No backup designated router on this network
|
||||
Multicast group memberships: OSPFAllRouters OSPFDesignatedRouters
|
||||
Timer intervals configured, Hello 10s, Dead 40s, Wait 40s, Retransmit 5
|
||||
Hello due in XX.XXXs
|
||||
Neighbor Count is 0, Adjacent neighbor count is 0
|
@ -0,0 +1,46 @@
|
||||
lo is up, type LOOPBACK
|
||||
Interface ID: 1
|
||||
OSPF not enabled on this interface
|
||||
r1-eth0 is up, type BROADCAST
|
||||
Interface ID: 2
|
||||
OSPF not enabled on this interface
|
||||
r1-eth1 is up, type BROADCAST
|
||||
Interface ID: 3
|
||||
OSPF not enabled on this interface
|
||||
r1-eth2 is up, type BROADCAST
|
||||
Interface ID: 4
|
||||
OSPF not enabled on this interface
|
||||
r1-eth3 is up, type BROADCAST
|
||||
Interface ID: 5
|
||||
OSPF not enabled on this interface
|
||||
r1-eth4 is up, type BROADCAST
|
||||
Interface ID: 6
|
||||
Internet Address:
|
||||
inet : 192.168.4.1/26
|
||||
inet6: fc00:0:0:4::1/64
|
||||
inet6: fe80::XXXX:XXXX:XXXX:XXXX/64
|
||||
Instance ID 0, Interface MTU 1500 (autodetect: 1500)
|
||||
MTU mismatch detection: enabled
|
||||
Area ID 0.0.0.0, Cost 10
|
||||
State DR, Transmit Delay 1 sec, Priority 1
|
||||
Timer intervals configured:
|
||||
Hello 10, Dead 40, Retransmit 5
|
||||
DR: 192.168.0.1 BDR: 0.0.0.0
|
||||
Number of I/F scoped LSAs is 1
|
||||
0 Pending LSAs for LSUpdate in Time 00:00:00 [thread off]
|
||||
0 Pending LSAs for LSAck in Time 00:00:00 [thread off]
|
||||
r1-eth5 is up, type BROADCAST
|
||||
Interface ID: 7
|
||||
OSPF not enabled on this interface
|
||||
r1-eth6 is up, type BROADCAST
|
||||
Interface ID: 8
|
||||
OSPF not enabled on this interface
|
||||
r1-eth7 is up, type BROADCAST
|
||||
Interface ID: 9
|
||||
OSPF not enabled on this interface
|
||||
r1-eth8 is up, type BROADCAST
|
||||
Interface ID: 10
|
||||
OSPF not enabled on this interface
|
||||
r1-eth9 is up, type BROADCAST
|
||||
Interface ID: 11
|
||||
OSPF not enabled on this interface
|
@ -0,0 +1,28 @@
|
||||
Area test:
|
||||
Interface: r1-eth5, State: Up, Active, Circuit Id: 0xXX
|
||||
Type: lan, Level: L1, SNPA: XXXX.XXXX.XXXX
|
||||
Level-1 Information:
|
||||
Metric: 10, Active neighbors: 0
|
||||
Hello interval: 3, Holddown count: 10 (pad)
|
||||
CNSP interval: 10, PSNP interval: 2
|
||||
LAN Priority: 64, is not DIS
|
||||
IP Prefix(es):
|
||||
192.168.5.1/26
|
||||
IPv6 Link-Locals:
|
||||
fe80::XXXX:XXXX:XXXX:XXXX/64
|
||||
IPv6 Prefixes:
|
||||
fc00:0:0:5::1/64
|
||||
|
||||
Interface: r1-eth6, State: Up, Active, Circuit Id: 0xXX
|
||||
Type: lan, Level: L2, SNPA: XXXX.XXXX.XXXX
|
||||
Level-2 Information:
|
||||
Metric: 10, Active neighbors: 0
|
||||
Hello interval: 3, Holddown count: 10 (pad)
|
||||
CNSP interval: 10, PSNP interval: 2
|
||||
LAN Priority: 64, is not DIS
|
||||
IP Prefix(es):
|
||||
192.168.6.1/26
|
||||
IPv6 Link-Locals:
|
||||
fe80::XXXX:XXXX:XXXX:XXXX/64
|
||||
IPv6 Prefixes:
|
||||
fc00:0:0:6::1/64
|
@ -0,0 +1,3 @@
|
||||
AF Interface State Uptime Hello Timers ac
|
||||
ipv4 r1-eth9 ACTIVE xx:xx:xx 5/15 0
|
||||
ipv6 r1-eth9 ACTIVE xx:xx:xx 5/15 0
|
72
tests/topotests/all-protocol-startup/r1/zebra.conf
Normal file
72
tests/topotests/all-protocol-startup/r1/zebra.conf
Normal file
@ -0,0 +1,72 @@
|
||||
log file zebra.log
|
||||
!
|
||||
hostname r1
|
||||
!
|
||||
interface r1-eth0
|
||||
description to sw0 - no routing protocol
|
||||
ip address 192.168.0.1/24
|
||||
ipv6 address fc00:0:0:0::1/64
|
||||
!
|
||||
interface r1-eth1
|
||||
description to sw1 - RIP interface
|
||||
ip address 192.168.1.1/26
|
||||
ipv6 address fc00:0:0:1::1/64
|
||||
no link-detect
|
||||
!
|
||||
interface r1-eth2
|
||||
description to sw2 - RIPng interface
|
||||
ip address 192.168.2.1/26
|
||||
ipv6 address fc00:0:0:2::1/64
|
||||
no link-detect
|
||||
!
|
||||
interface r1-eth3
|
||||
description to sw3 - OSPFv2 interface
|
||||
ip address 192.168.3.1/26
|
||||
ipv6 address fc00:0:0:3::1/64
|
||||
no link-detect
|
||||
!
|
||||
interface r1-eth4
|
||||
description to sw4 - OSPFv3 interface
|
||||
ip address 192.168.4.1/26
|
||||
ipv6 address fc00:0:0:4::1/64
|
||||
no link-detect
|
||||
!
|
||||
interface r1-eth5
|
||||
description to sw5 - ISIS IPv4 interface
|
||||
ip address 192.168.5.1/26
|
||||
ipv6 address fc00:0:0:5::1/64
|
||||
no link-detect
|
||||
!
|
||||
interface r1-eth6
|
||||
description to sw6 - ISIS IPv6 interface
|
||||
ip address 192.168.6.1/26
|
||||
ipv6 address fc00:0:0:6::1/64
|
||||
no link-detect
|
||||
!
|
||||
interface r1-eth7
|
||||
description to sw7 - BGP IPv4 interface
|
||||
ip address 192.168.7.1/26
|
||||
ipv6 address fc00:0:0:7::1/64
|
||||
no link-detect
|
||||
!
|
||||
interface r1-eth8
|
||||
description to sw8 - BGP IPv6 interface
|
||||
ip address 192.168.8.1/26
|
||||
ipv6 address fc00:0:0:8::1/64
|
||||
no link-detect
|
||||
!
|
||||
interface r1-eth9
|
||||
description to sw9 - LDP interface
|
||||
ip address 192.168.9.1/26
|
||||
ipv6 address fc00:0:0:9::1/64
|
||||
|
||||
no link-detect
|
||||
!
|
||||
!
|
||||
ip forwarding
|
||||
ipv6 forwarding
|
||||
!
|
||||
!
|
||||
line vty
|
||||
!
|
||||
|
@ -0,0 +1,61 @@
|
||||
## GraphViz file for test_all_protocol_startup
|
||||
##
|
||||
## Color coding:
|
||||
#########################
|
||||
## Main FRR: #f08080 red
|
||||
## No protocol: #d0e0d0 gray
|
||||
## RIP: #19e3d9 Cyan
|
||||
## RIPng: #fcb314 dark yellow
|
||||
## OSPFv2: #32b835 Green
|
||||
## OSPFv3: #19e3d9 Cyan
|
||||
## ISIS IPv4 #33ff99 light green
|
||||
## ISIS IPv6 #9a81ec purple
|
||||
## BGP IPv4 #eee3d3 beige
|
||||
## BGP IPv6 #fdff00 yellow
|
||||
## LDP IPv4 #fedbe2 light pink
|
||||
##### Colors (see http://www.color-hex.com/)
|
||||
|
||||
graph test_all_protocol_startup {
|
||||
|
||||
// title
|
||||
labelloc="t";
|
||||
label="Test Topologoy All Protocols Startup";
|
||||
|
||||
######################
|
||||
# Routers
|
||||
######################
|
||||
|
||||
# Main FRR Router with all protocols
|
||||
R1 [shape=doubleoctagon, label="R1 FRR\nMain Router", fillcolor="#f08080", style=filled];
|
||||
|
||||
######################
|
||||
# Network Lists
|
||||
######################
|
||||
|
||||
SW0_STUB [label="SW0 (no protocol)\n192.168.1.0/24\nfc00:0:0:0::/64", fillcolor="#d0e0d0", style=filled];
|
||||
|
||||
SW1_RIP [label="SW1 RIP\n192.168.1.0/24\nfc00:0:0:1::/64", fillcolor="#19e3d9", style=filled];
|
||||
SW2_RIPNG [label="SW2 RIPng\n192.168.2.0/24\nfc00:0:0:2::/64", fillcolor="#fcb314", style=filled];
|
||||
SW3_OSPF [label="SW3 OSPFv2\n192.168.3.0/24\nfc00:0:0:3::/64", fillcolor="#32b835", style=filled];
|
||||
SW4_OSPFV3 [label="SW4 OSPFv3\n192.168.4.0/24\nfc00:0:0:4::/64", fillcolor="#19e3d9", style=filled];
|
||||
SW5_ISIS_V4 [label="SW5 ISIS IPv4\n192.168.5.0/24\nfc00:0:0:5::/64", fillcolor="#33ff99", style=filled];
|
||||
SW6_ISIS_V6 [label="SW6 ISIS IPv6\n192.168.6.0/24\nfc00:0:0:6::/64", fillcolor="#9a81ec", style=filled];
|
||||
SW7_BGP_V4 [label="SW7 BGP IPv4\n192.168.7.0/24\nfc00:0:0:7::/64", fillcolor="#eee3d3", style=filled];
|
||||
SW8_BGP_V6 [label="SW8 BGP IPv6\n192.168.8.0/24\nfc00:0:0:8::/64", fillcolor="#fdff00", style=filled];
|
||||
SW9_LDP [label="SW9 LDP\n192.168.9.0/24\nfc00:0:0:9::/64", fillcolor="#fedbe2", style=filled];
|
||||
|
||||
######################
|
||||
# Network Connections
|
||||
######################
|
||||
R1 -- SW0_STUB [label = "eth0\n.1\n::1"];
|
||||
R1 -- SW1_RIP [label = "eth1\n.1\n::1"];
|
||||
R1 -- SW2_RIPNG [label = "eth2\n.1\n::1"];
|
||||
R1 -- SW3_OSPF [label = "eth3\n.1\n::1"];
|
||||
R1 -- SW4_OSPFV3 [label = "eth4\n.1\n::1"];
|
||||
R1 -- SW5_ISIS_V4 [label = "eth5\n.1\n::1"];
|
||||
R1 -- SW6_ISIS_V6 [label = "eth6\n.1\n::1"];
|
||||
R1 -- SW7_BGP_V4 [label = "eth7\n.1\n::1"];
|
||||
R1 -- SW8_BGP_V6 [label = "eth8\n.1\n::1"];
|
||||
R1 -- SW9_LDP [label = "eth9\n.1\n::1"];
|
||||
|
||||
}
|
Binary file not shown.
951
tests/topotests/all-protocol-startup/test_all_protocol_startup.py
Executable file
951
tests/topotests/all-protocol-startup/test_all_protocol_startup.py
Executable file
@ -0,0 +1,951 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# test_all_protocol_startup.py
|
||||
# Part of NetDEF Topology Tests
|
||||
#
|
||||
# Copyright (c) 2017 by
|
||||
# Network Device Education Foundation, Inc. ("NetDEF")
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software
|
||||
# for any purpose with or without fee is hereby granted, provided
|
||||
# that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
||||
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
# OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
"""
|
||||
test_all_protocol_startup.py: Test of all protocols at same time
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import pytest
|
||||
import glob
|
||||
from time import sleep
|
||||
|
||||
from mininet.topo import Topo
|
||||
from mininet.net import Mininet
|
||||
from mininet.node import Node, OVSSwitch, Host
|
||||
from mininet.log import setLogLevel, info
|
||||
from mininet.cli import CLI
|
||||
from mininet.link import Intf
|
||||
|
||||
from functools import partial
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from lib import topotest
|
||||
|
||||
fatal_error = ""
|
||||
|
||||
|
||||
#####################################################
|
||||
##
|
||||
## Network Topology Definition
|
||||
##
|
||||
#####################################################
|
||||
|
||||
class NetworkTopo(Topo):
|
||||
"All Protocol Startup Test"
|
||||
|
||||
def build(self, **_opts):
|
||||
|
||||
# Setup Routers
|
||||
router = {}
|
||||
#
|
||||
# Setup Main Router
|
||||
router[1] = topotest.addRouter(self, 'r1')
|
||||
#
|
||||
|
||||
# Setup Switches
|
||||
switch = {}
|
||||
#
|
||||
for i in range(0, 10):
|
||||
switch[i] = self.addSwitch('sw%s' % i, cls=topotest.LegacySwitch)
|
||||
self.addLink(switch[i], router[1], intfName2='r1-eth%s' % i )
|
||||
|
||||
|
||||
#####################################################
|
||||
##
|
||||
## Tests starting
|
||||
##
|
||||
#####################################################
|
||||
|
||||
def setup_module(module):
|
||||
global topo, net
|
||||
global fatal_error
|
||||
|
||||
print("\n\n** %s: Setup Topology" % module.__name__)
|
||||
print("******************************************\n")
|
||||
|
||||
print("Cleanup old Mininet runs")
|
||||
os.system('sudo mn -c > /dev/null 2>&1')
|
||||
os.system('sudo rm /tmp/r* > /dev/null 2>&1')
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
topo = NetworkTopo()
|
||||
|
||||
net = Mininet(controller=None, topo=topo)
|
||||
net.start()
|
||||
|
||||
if net['r1'].get_routertype() != 'frr':
|
||||
fatal_error = "Test is only implemented for FRR"
|
||||
sys.stderr.write('\n\nTest is only implemented for FRR - Skipping\n\n')
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
# Starting Routers
|
||||
#
|
||||
# Main router
|
||||
for i in range(1, 2):
|
||||
net['r%s' % i].loadConf('zebra', '%s/r%s/zebra.conf' % (thisDir, i))
|
||||
net['r%s' % i].loadConf('ripd', '%s/r%s/ripd.conf' % (thisDir, i))
|
||||
net['r%s' % i].loadConf('ripngd', '%s/r%s/ripngd.conf' % (thisDir, i))
|
||||
net['r%s' % i].loadConf('ospfd', '%s/r%s/ospfd.conf' % (thisDir, i))
|
||||
if net['r1'].checkRouterVersion('<', '4.0'):
|
||||
net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf-pre-v4' % (thisDir, i))
|
||||
else:
|
||||
net['r%s' % i].loadConf('ospf6d', '%s/r%s/ospf6d.conf' % (thisDir, i))
|
||||
net['r%s' % i].loadConf('isisd', '%s/r%s/isisd.conf' % (thisDir, i))
|
||||
net['r%s' % i].loadConf('bgpd', '%s/r%s/bgpd.conf' % (thisDir, i))
|
||||
if net['r%s' % i].daemon_available('ldpd'):
|
||||
# Only test LDPd if it's installed and Kernel >= 4.5
|
||||
net['r%s' % i].loadConf('ldpd', '%s/r%s/ldpd.conf' % (thisDir, i))
|
||||
net['r%s' % i].startRouter()
|
||||
|
||||
# For debugging after starting Quagga/FRR daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def teardown_module(module):
|
||||
global net
|
||||
|
||||
print("\n\n** %s: Shutdown Topology" % module.__name__)
|
||||
print("******************************************\n")
|
||||
|
||||
# End - Shutdown network
|
||||
net.stop()
|
||||
|
||||
|
||||
def test_router_running():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
print("\n\n** Check if FRR/Quagga is running on each Router node")
|
||||
print("******************************************\n")
|
||||
sleep(5)
|
||||
|
||||
# Starting Routers
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_error_messages_vtysh():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
print("\n\n** Check for error messages on VTYSH")
|
||||
print("******************************************\n")
|
||||
|
||||
failures = 0
|
||||
for i in range(1, 2):
|
||||
#
|
||||
# First checking Standard Output
|
||||
#
|
||||
|
||||
# VTYSH output from router
|
||||
vtystdout = net['r%s' % i].cmd('vtysh -c "show version" 2> /dev/null').rstrip()
|
||||
|
||||
# Fix newlines (make them all the same)
|
||||
vtystdout = ('\n'.join(vtystdout.splitlines()) + '\n').rstrip()
|
||||
# Drop everything starting with "FRRouting X.xx" message
|
||||
vtystdout = re.sub(r"FRRouting [0-9]+.*", "", vtystdout, flags=re.DOTALL)
|
||||
|
||||
if (vtystdout == ''):
|
||||
print("r%s StdOut ok" % i)
|
||||
|
||||
assert vtystdout == '', "Vtysh StdOut Output check failed for router r%s" % i
|
||||
|
||||
#
|
||||
# Second checking Standard Error
|
||||
#
|
||||
|
||||
# VTYSH StdErr output from router
|
||||
vtystderr = net['r%s' % i].cmd('vtysh -c "show version" > /dev/null').rstrip()
|
||||
|
||||
# Fix newlines (make them all the same)
|
||||
vtystderr = ('\n'.join(vtystderr.splitlines()) + '\n').rstrip()
|
||||
# # Drop everything starting with "FRRouting X.xx" message
|
||||
# vtystderr = re.sub(r"FRRouting [0-9]+.*", "", vtystderr, flags=re.DOTALL)
|
||||
|
||||
if (vtystderr == ''):
|
||||
print("r%s StdErr ok" % i)
|
||||
|
||||
assert vtystderr == '', "Vtysh StdErr Output check failed for router r%s" % i
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_error_messages_daemons():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
print("\n\n** Check for error messages in daemons")
|
||||
print("******************************************\n")
|
||||
|
||||
error_logs = ""
|
||||
|
||||
for i in range(1, 2):
|
||||
log = net['r%s' % i].getStdErr('ripd')
|
||||
if log:
|
||||
error_logs += "r%s RIPd StdErr Output:\n" % i
|
||||
error_logs += log
|
||||
log = net['r%s' % i].getStdErr('ripngd')
|
||||
if log:
|
||||
error_logs += "r%s RIPngd StdErr Output:\n" % i
|
||||
error_logs += log
|
||||
log = net['r%s' % i].getStdErr('ospfd')
|
||||
if log:
|
||||
error_logs += "r%s OSPFd StdErr Output:\n" % i
|
||||
error_logs += log
|
||||
log = net['r%s' % i].getStdErr('ospf6d')
|
||||
if log:
|
||||
error_logs += "r%s OSPF6d StdErr Output:\n" % i
|
||||
error_logs += log
|
||||
log = net['r%s' % i].getStdErr('isisd')
|
||||
# ISIS shows debugging enabled status on StdErr
|
||||
# Remove these messages
|
||||
log = re.sub(r"^IS-IS .* debugging is on.*", "", log).rstrip()
|
||||
if log:
|
||||
error_logs += "r%s ISISd StdErr Output:\n" % i
|
||||
error_logs += log
|
||||
log = net['r%s' % i].getStdErr('bgpd')
|
||||
if log:
|
||||
error_logs += "r%s BGPd StdErr Output:\n" % i
|
||||
error_logs += log
|
||||
if (net['r%s' % i].daemon_available('ldpd')):
|
||||
log = net['r%s' % i].getStdErr('ldpd')
|
||||
if log:
|
||||
error_logs += "r%s LDPd StdErr Output:\n" % i
|
||||
error_logs += log
|
||||
log = net['r%s' % i].getStdErr('zebra')
|
||||
if log:
|
||||
error_logs += "r%s Zebra StdErr Output:\n"
|
||||
error_logs += log
|
||||
|
||||
if error_logs:
|
||||
sys.stderr.write('Failed check for StdErr Output on daemons:\n%s\n' % error_logs)
|
||||
|
||||
# Ignoring the issue if told to ignore (ie not yet fixed)
|
||||
if (error_logs != ""):
|
||||
if (os.environ.get('bamboo_TOPOTESTS_ISSUE_349') == "IGNORE"):
|
||||
sys.stderr.write('Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349\n')
|
||||
pytest.skip('Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349')
|
||||
|
||||
assert error_logs == "", "Daemons report errors to StdErr"
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_converge_protocols():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Waiting for protocols convergence")
|
||||
print("******************************************\n")
|
||||
|
||||
# Not really implemented yet - just sleep 60 secs for now
|
||||
sleep(60)
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
## CLI(net)
|
||||
|
||||
|
||||
def test_rip_status():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying RIP status")
|
||||
print("******************************************\n")
|
||||
failures = 0
|
||||
for i in range(1, 2):
|
||||
refTableFile = '%s/r%s/rip_status.ref' % (thisDir, i)
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show ip rip status" 2> /dev/null').rstrip()
|
||||
# Drop time in next due
|
||||
actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
|
||||
# Drop time in last update
|
||||
actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual IP RIP status",
|
||||
title2="expected IP RIP status")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
sys.stderr.write('r%s failed IP RIP status check:\n%s\n' % (i, diff))
|
||||
failures += 1
|
||||
else:
|
||||
print("r%s ok" % i)
|
||||
|
||||
assert failures == 0, "IP RIP status failed for router r%s:\n%s" % (i, diff)
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_ripng_status():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying RIPng status")
|
||||
print("******************************************\n")
|
||||
failures = 0
|
||||
for i in range(1, 2):
|
||||
refTableFile = '%s/r%s/ripng_status.ref' % (thisDir, i)
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null').rstrip()
|
||||
# Mask out Link-Local mac address portion. They are random...
|
||||
actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual)
|
||||
# Drop time in next due
|
||||
actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
|
||||
# Drop time in last update
|
||||
actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual IPv6 RIPng status",
|
||||
title2="expected IPv6 RIPng status")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
sys.stderr.write('r%s failed IPv6 RIPng status check:\n%s\n' % (i, diff))
|
||||
failures += 1
|
||||
else:
|
||||
print("r%s ok" % i)
|
||||
|
||||
assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % (i, diff)
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_ospfv2_interfaces():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying OSPFv2 interfaces")
|
||||
print("******************************************\n")
|
||||
failures = 0
|
||||
for i in range(1, 2):
|
||||
refTableFile = '%s/r%s/show_ip_ospf_interface.ref' % (thisDir, i)
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show ip ospf interface" 2> /dev/null').rstrip()
|
||||
# Mask out Bandwidth portion. They may change..
|
||||
actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual)
|
||||
# Drop time in next due
|
||||
actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual)
|
||||
# Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both
|
||||
actual = re.sub(r"MTU mismatch detection:([a-z]+.*)", r"MTU mismatch detection: \1", actual)
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual SHOW IP OSPF INTERFACE",
|
||||
title2="expected SHOW IP OSPF INTERFACE")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
sys.stderr.write('r%s failed SHOW IP OSPF INTERFACE check:\n%s\n' % (i, diff))
|
||||
failures += 1
|
||||
else:
|
||||
print("r%s ok" % i)
|
||||
|
||||
# Ignoring the issue if told to ignore (ie not yet fixed)
|
||||
if (failures != 0):
|
||||
if (os.environ.get('bamboo_TOPOTESTS_ISSUE_348') == "IGNORE"):
|
||||
sys.stderr.write('Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348\n')
|
||||
pytest.skip('Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348')
|
||||
|
||||
assert failures == 0, "SHOW IP OSPF INTERFACE failed for router r%s:\n%s" % (i, diff)
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_isis_interfaces():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying ISIS interfaces")
|
||||
print("******************************************\n")
|
||||
failures = 0
|
||||
for i in range(1, 2):
|
||||
refTableFile = '%s/r%s/show_isis_interface_detail.ref' % (thisDir, i)
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show isis interface detail" 2> /dev/null').rstrip()
|
||||
# Mask out Link-Local mac address portion. They are random...
|
||||
actual = re.sub(r"fe80::[0-9a-f:]+", "fe80::XXXX:XXXX:XXXX:XXXX", actual)
|
||||
# Mask out SNPA mac address portion. They are random...
|
||||
actual = re.sub(r"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual)
|
||||
# Mask out Circuit ID number
|
||||
actual = re.sub(r"Circuit Id: 0x[0-9]+", "Circuit Id: 0xXX", actual)
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual SHOW ISIS INTERFACE DETAIL",
|
||||
title2="expected SHOW ISIS OSPF6 INTERFACE DETAIL")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
sys.stderr.write('r%s failed SHOW ISIS INTERFACE DETAIL check:\n%s\n' % (i, diff))
|
||||
failures += 1
|
||||
else:
|
||||
print("r%s ok" % i)
|
||||
|
||||
assert failures == 0, "SHOW ISIS INTERFACE DETAIL failed for router r%s:\n%s" % (i, diff)
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_bgp_summary():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying BGP Summary")
|
||||
print("******************************************\n")
|
||||
failures = 0
|
||||
for i in range(1, 2):
|
||||
refTableFile = '%s/r%s/show_ip_bgp_summary.ref' % (thisDir, i)
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show ip bgp summary" 2> /dev/null').rstrip()
|
||||
# Mask out "using XXiXX bytes" portion. They are random...
|
||||
actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
|
||||
# Mask out "using XiXXX KiB" portion. They are random...
|
||||
actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
|
||||
#
|
||||
# Remove extra summaries which exist with newer versions
|
||||
#
|
||||
# Remove summary lines (changed recently)
|
||||
actual = re.sub(r'Total number.*', '', actual)
|
||||
actual = re.sub(r'Displayed.*', '', actual)
|
||||
# Remove IPv4 Unicast Summary (Title only)
|
||||
actual = re.sub(r'IPv4 Unicast Summary:', '', actual)
|
||||
# Remove IPv4 Multicast Summary (all of it)
|
||||
actual = re.sub(r'IPv4 Multicast Summary:', '', actual)
|
||||
actual = re.sub(r'No IPv4 Multicast neighbor is configured', '', actual)
|
||||
# Remove IPv4 VPN Summary (all of it)
|
||||
actual = re.sub(r'IPv4 VPN Summary:', '', actual)
|
||||
actual = re.sub(r'No IPv4 VPN neighbor is configured', '', actual)
|
||||
# Remove IPv4 Encap Summary (all of it)
|
||||
actual = re.sub(r'IPv4 Encap Summary:', '', actual)
|
||||
actual = re.sub(r'No IPv4 Encap neighbor is configured', '', actual)
|
||||
# Remove Unknown Summary (all of it)
|
||||
actual = re.sub(r'Unknown Summary:', '', actual)
|
||||
actual = re.sub(r'No Unknown neighbor is configured', '', actual)
|
||||
|
||||
actual = re.sub(r'IPv4 labeled-unicast Summary:', '', actual)
|
||||
actual = re.sub(r'No IPv4 labeled-unicast neighbor is configured', '', actual)
|
||||
|
||||
# Strip empty lines
|
||||
actual = actual.lstrip()
|
||||
actual = actual.rstrip()
|
||||
#
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual SHOW IP BGP SUMMARY",
|
||||
title2="expected SHOW IP BGP SUMMARY")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
sys.stderr.write('r%s failed SHOW IP BGP SUMMARY check:\n%s\n' % (i, diff))
|
||||
failures += 1
|
||||
else:
|
||||
print("r%s ok" % i)
|
||||
|
||||
assert failures == 0, "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % (i, diff)
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_bgp_ipv6_summary():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying BGP IPv6 Summary")
|
||||
print("******************************************\n")
|
||||
failures = 0
|
||||
for i in range(1, 2):
|
||||
refTableFile = '%s/r%s/show_bgp_ipv6_summary.ref' % (thisDir, i)
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null').rstrip()
|
||||
# Mask out "using XXiXX bytes" portion. They are random...
|
||||
actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
|
||||
# Mask out "using XiXXX KiB" portion. They are random...
|
||||
actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
|
||||
#
|
||||
# Remove extra summaries which exist with newer versions
|
||||
#
|
||||
# Remove summary lines (changed recently)
|
||||
actual = re.sub(r'Total number.*', '', actual)
|
||||
actual = re.sub(r'Displayed.*', '', actual)
|
||||
# Remove IPv4 Unicast Summary (Title only)
|
||||
actual = re.sub(r'IPv6 Unicast Summary:', '', actual)
|
||||
# Remove IPv4 Multicast Summary (all of it)
|
||||
actual = re.sub(r'IPv6 Multicast Summary:', '', actual)
|
||||
actual = re.sub(r'No IPv6 Multicast neighbor is configured', '', actual)
|
||||
# Remove IPv4 VPN Summary (all of it)
|
||||
actual = re.sub(r'IPv6 VPN Summary:', '', actual)
|
||||
actual = re.sub(r'No IPv6 VPN neighbor is configured', '', actual)
|
||||
# Remove IPv4 Encap Summary (all of it)
|
||||
actual = re.sub(r'IPv6 Encap Summary:', '', actual)
|
||||
actual = re.sub(r'No IPv6 Encap neighbor is configured', '', actual)
|
||||
# Remove Unknown Summary (all of it)
|
||||
actual = re.sub(r'Unknown Summary:', '', actual)
|
||||
actual = re.sub(r'No Unknown neighbor is configured', '', actual)
|
||||
|
||||
# Remove Labeled Unicast Summary (all of it)
|
||||
actual = re.sub(r'IPv6 labeled-unicast Summary:', '', actual)
|
||||
actual = re.sub(r'No IPv6 labeled-unicast neighbor is configured', '', actual)
|
||||
|
||||
# Strip empty lines
|
||||
actual = actual.lstrip()
|
||||
actual = actual.rstrip()
|
||||
#
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual SHOW BGP IPv6 SUMMARY",
|
||||
title2="expected SHOW BGP IPv6 SUMMARY")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
sys.stderr.write('r%s failed SHOW BGP IPv6 SUMMARY check:\n%s\n' % (i, diff))
|
||||
failures += 1
|
||||
else:
|
||||
print("r%s ok" % i)
|
||||
|
||||
assert failures == 0, "SHOW BGP IPv6 SUMMARY failed for router r%s:\n%s" % (i, diff)
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_bgp_ipv4():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying BGP IPv4")
|
||||
print("******************************************\n")
|
||||
diffresult = {}
|
||||
for i in range(1, 2):
|
||||
success = 0
|
||||
for refTableFile in (glob.glob(
|
||||
'%s/r%s/show_bgp_ipv4*.ref' % (thisDir, i))):
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip()
|
||||
# Remove summary line (changed recently)
|
||||
actual = re.sub(r'Total number.*', '', actual)
|
||||
actual = re.sub(r'Displayed.*', '', actual)
|
||||
actual = actual.rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual SHOW BGP IPv4",
|
||||
title2="expected SHOW BGP IPv4")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
diffresult[refTableFile] = diff
|
||||
else:
|
||||
success = 1
|
||||
print("template %s matched: r%s ok" % (refTableFile, i))
|
||||
break
|
||||
|
||||
if not success:
|
||||
resultstr = 'No template matched.\n'
|
||||
for f in diffresult.iterkeys():
|
||||
resultstr += (
|
||||
'template %s: r%s failed SHOW BGP IPv4 check:\n%s\n'
|
||||
% (f, i, diffresult[f]))
|
||||
raise AssertionError(
|
||||
"SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr))
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_bgp_ipv6():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying BGP IPv6")
|
||||
print("******************************************\n")
|
||||
diffresult = {}
|
||||
for i in range(1, 2):
|
||||
success = 0
|
||||
for refTableFile in (glob.glob(
|
||||
'%s/r%s/show_bgp_ipv6*.ref' % (thisDir, i))):
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip()
|
||||
# Remove summary line (changed recently)
|
||||
actual = re.sub(r'Total number.*', '', actual)
|
||||
actual = re.sub(r'Displayed.*', '', actual)
|
||||
actual = actual.rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual SHOW BGP IPv6",
|
||||
title2="expected SHOW BGP IPv6")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
diffresult[refTableFile] = diff
|
||||
else:
|
||||
success = 1
|
||||
print("template %s matched: r%s ok" % (refTableFile, i))
|
||||
|
||||
if not success:
|
||||
resultstr = 'No template matched.\n'
|
||||
for f in diffresult.iterkeys():
|
||||
resultstr += (
|
||||
'template %s: r%s failed SHOW BGP IPv6 check:\n%s\n'
|
||||
% (f, i, diffresult[f]))
|
||||
raise AssertionError(
|
||||
"SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr))
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
|
||||
def test_mpls_interfaces():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
# Skip if no LDP installed or old kernel
|
||||
if (net['r1'].daemon_available('ldpd') == False):
|
||||
pytest.skip("No MPLS or kernel < 4.5")
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("\n\n** Verifying MPLS Interfaces")
|
||||
print("******************************************\n")
|
||||
failures = 0
|
||||
for i in range(1, 2):
|
||||
refTableFile = '%s/r%s/show_mpls_ldp_interface.ref' % (thisDir, i)
|
||||
if os.path.isfile(refTableFile):
|
||||
# Read expected result from file
|
||||
expected = open(refTableFile).read().rstrip()
|
||||
# Fix newlines (make them all the same)
|
||||
expected = ('\n'.join(expected.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Actual output from router
|
||||
actual = net['r%s' % i].cmd('vtysh -c "show mpls ldp interface" 2> /dev/null').rstrip()
|
||||
# Mask out Timer in Uptime
|
||||
actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual)
|
||||
# Fix newlines (make them all the same)
|
||||
actual = ('\n'.join(actual.splitlines()) + '\n').splitlines(1)
|
||||
|
||||
# Generate Diff
|
||||
diff = topotest.get_textdiff(actual, expected,
|
||||
title1="actual MPLS LDP interface status",
|
||||
title2="expected MPLS LDP interface status")
|
||||
|
||||
# Empty string if it matches, otherwise diff contains unified diff
|
||||
if diff:
|
||||
sys.stderr.write('r%s failed MPLS LDP Interface status Check:\n%s\n' % (i, diff))
|
||||
failures += 1
|
||||
else:
|
||||
print("r%s ok" % i)
|
||||
|
||||
if failures>0:
|
||||
fatal_error = "MPLS LDP Interface status failed"
|
||||
|
||||
assert failures == 0, "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff)
|
||||
|
||||
# Make sure that all daemons are running
|
||||
for i in range(1, 2):
|
||||
fatal_error = net['r%s' % i].checkRouterRunning()
|
||||
assert fatal_error == "", fatal_error
|
||||
|
||||
# For debugging after starting FRR/Quagga daemons, uncomment the next line
|
||||
# CLI(net)
|
||||
|
||||
|
||||
def test_shutdown_check_stderr():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
print("\n\n** Verifying unexpected STDERR output from daemons")
|
||||
print("******************************************\n")
|
||||
|
||||
if os.environ.get('TOPOTESTS_CHECK_STDERR') is None:
|
||||
print("SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n")
|
||||
pytest.skip('Skipping test for Stderr output')
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
print("thisDir=" + thisDir)
|
||||
|
||||
net['r1'].stopRouter()
|
||||
|
||||
log = net['r1'].getStdErr('ripd')
|
||||
if log:
|
||||
print("\nRIPd StdErr Log:\n" + log)
|
||||
log = net['r1'].getStdErr('ripngd')
|
||||
if log:
|
||||
print("\nRIPngd StdErr Log:\n" + log)
|
||||
log = net['r1'].getStdErr('ospfd')
|
||||
if log:
|
||||
print("\nOSPFd StdErr Log:\n" + log)
|
||||
log = net['r1'].getStdErr('ospf6d')
|
||||
if log:
|
||||
print("\nOSPF6d StdErr Log:\n" + log)
|
||||
log = net['r1'].getStdErr('isisd')
|
||||
if log:
|
||||
print("\nISISd StdErr Log:\n" + log)
|
||||
log = net['r1'].getStdErr('bgpd')
|
||||
if log:
|
||||
print("\nBGPd StdErr Log:\n" + log)
|
||||
if (net['r1'].daemon_available('ldpd')):
|
||||
log = net['r1'].getStdErr('ldpd')
|
||||
if log:
|
||||
print("\nLDPd StdErr Log:\n" + log)
|
||||
log = net['r1'].getStdErr('zebra')
|
||||
if log:
|
||||
print("\nZebra StdErr Log:\n" + log)
|
||||
|
||||
|
||||
def test_shutdown_check_memleak():
|
||||
global fatal_error
|
||||
global net
|
||||
|
||||
# Skip if previous fatal error condition is raised
|
||||
if (fatal_error != ""):
|
||||
pytest.skip(fatal_error)
|
||||
|
||||
if os.environ.get('TOPOTESTS_CHECK_MEMLEAK') is None:
|
||||
print("SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n")
|
||||
pytest.skip('Skipping test for memory leaks')
|
||||
|
||||
thisDir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
for i in range(1, 2):
|
||||
net['r%s' % i].stopRouter()
|
||||
net['r%s' % i].report_memory_leaks(os.environ.get('TOPOTESTS_CHECK_MEMLEAK'), os.path.basename(__file__))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
setLogLevel('info')
|
||||
# To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
|
||||
# retval = pytest.main(["-s", "--tb=no"])
|
||||
retval = pytest.main(["-s"])
|
||||
sys.exit(retval)
|
0
tests/topotests/bfd-topo1/__init__.py
Normal file
0
tests/topotests/bfd-topo1/__init__.py
Normal file
6
tests/topotests/bfd-topo1/r1/bfdd.conf
Normal file
6
tests/topotests/bfd-topo1/r1/bfdd.conf
Normal file
@ -0,0 +1,6 @@
|
||||
bfd
|
||||
peer 192.168.0.2
|
||||
echo-mode
|
||||
no shutdown
|
||||
!
|
||||
!
|
52
tests/topotests/bfd-topo1/r1/bgp_prefixes.json
Normal file
52
tests/topotests/bfd-topo1/r1/bgp_prefixes.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"routes": {
|
||||
"10.254.254.2/32": [
|
||||
{
|
||||
"aspath": "102",
|
||||
"prefix": "10.254.254.2",
|
||||
"valid": true,
|
||||
"peerId": "192.168.0.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.0.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.254.254.3/32": [
|
||||
{
|
||||
"aspath": "102 103",
|
||||
"prefix": "10.254.254.3",
|
||||
"valid": true,
|
||||
"peerId": "192.168.0.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.0.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.254.254.4/32": [
|
||||
{
|
||||
"aspath": "102 104",
|
||||
"prefix": "10.254.254.4",
|
||||
"valid": true,
|
||||
"peerId": "192.168.0.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.0.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
11
tests/topotests/bfd-topo1/r1/bgp_summary.json
Normal file
11
tests/topotests/bfd-topo1/r1/bgp_summary.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ipv4Unicast": {
|
||||
"as": 101,
|
||||
"peers": {
|
||||
"192.168.0.2": {
|
||||
"remoteAs": 102,
|
||||
"state": "Established"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
tests/topotests/bfd-topo1/r1/bgpd.conf
Normal file
7
tests/topotests/bfd-topo1/r1/bgpd.conf
Normal file
@ -0,0 +1,7 @@
|
||||
router bgp 101
|
||||
neighbor 192.168.0.2 remote-as 102
|
||||
neighbor 192.168.0.2 bfd
|
||||
address-family ipv4 unicast
|
||||
network 10.254.254.1/32
|
||||
exit-address-family
|
||||
!
|
8
tests/topotests/bfd-topo1/r1/peers.json
Normal file
8
tests/topotests/bfd-topo1/r1/peers.json
Normal file
@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"remote-receive-interval": 1000,
|
||||
"remote-transmit-interval": 500,
|
||||
"peer": "192.168.0.2",
|
||||
"status": "up"
|
||||
}
|
||||
]
|
3
tests/topotests/bfd-topo1/r1/zebra.conf
Normal file
3
tests/topotests/bfd-topo1/r1/zebra.conf
Normal file
@ -0,0 +1,3 @@
|
||||
interface r1-eth0
|
||||
ip address 192.168.0.1/24
|
||||
!
|
12
tests/topotests/bfd-topo1/r2/bfdd.conf
Normal file
12
tests/topotests/bfd-topo1/r2/bfdd.conf
Normal file
@ -0,0 +1,12 @@
|
||||
bfd
|
||||
peer 192.168.0.1
|
||||
receive-interval 1000
|
||||
transmit-interval 500
|
||||
echo-mode
|
||||
no shutdown
|
||||
!
|
||||
peer 192.168.1.1
|
||||
echo-mode
|
||||
no shutdown
|
||||
!
|
||||
!
|
52
tests/topotests/bfd-topo1/r2/bgp_prefixes.json
Normal file
52
tests/topotests/bfd-topo1/r2/bgp_prefixes.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"routes": {
|
||||
"10.254.254.1/32": [
|
||||
{
|
||||
"aspath": "101",
|
||||
"prefix": "10.254.254.1",
|
||||
"valid": true,
|
||||
"peerId": "192.168.0.1",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.0.1",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.254.254.3/32": [
|
||||
{
|
||||
"aspath": "103",
|
||||
"prefix": "10.254.254.3",
|
||||
"valid": true,
|
||||
"peerId": "192.168.1.1",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.1.1",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.254.254.4/32": [
|
||||
{
|
||||
"aspath": "104",
|
||||
"prefix": "10.254.254.4",
|
||||
"valid": true,
|
||||
"peerId": "192.168.2.1",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.2.1",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
19
tests/topotests/bfd-topo1/r2/bgp_summary.json
Normal file
19
tests/topotests/bfd-topo1/r2/bgp_summary.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"ipv4Unicast": {
|
||||
"as": 102,
|
||||
"peers": {
|
||||
"192.168.0.1": {
|
||||
"remoteAs": 101,
|
||||
"state": "Established"
|
||||
},
|
||||
"192.168.1.1": {
|
||||
"remoteAs": 103,
|
||||
"state": "Established"
|
||||
},
|
||||
"192.168.2.1": {
|
||||
"remoteAs": 104,
|
||||
"state": "Established"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
tests/topotests/bfd-topo1/r2/bgpd.conf
Normal file
11
tests/topotests/bfd-topo1/r2/bgpd.conf
Normal file
@ -0,0 +1,11 @@
|
||||
router bgp 102
|
||||
neighbor 192.168.0.1 remote-as 101
|
||||
neighbor 192.168.0.1 bfd
|
||||
neighbor 192.168.1.1 remote-as 103
|
||||
neighbor 192.168.1.1 bfd
|
||||
neighbor 192.168.2.1 remote-as 104
|
||||
neighbor 192.168.2.1 bfd
|
||||
address-family ipv4 unicast
|
||||
network 10.254.254.2/32
|
||||
exit-address-family
|
||||
!
|
17
tests/topotests/bfd-topo1/r2/peers.json
Normal file
17
tests/topotests/bfd-topo1/r2/peers.json
Normal file
@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"peer": "192.168.0.1",
|
||||
"status": "up"
|
||||
},
|
||||
{
|
||||
"remote-echo-interval": 100,
|
||||
"peer": "192.168.1.1",
|
||||
"status": "up"
|
||||
},
|
||||
{
|
||||
"remote-transmit-interval": 2000,
|
||||
"remote-receive-interval": 2000,
|
||||
"peer": "192.168.2.1",
|
||||
"status": "up"
|
||||
}
|
||||
]
|
9
tests/topotests/bfd-topo1/r2/zebra.conf
Normal file
9
tests/topotests/bfd-topo1/r2/zebra.conf
Normal file
@ -0,0 +1,9 @@
|
||||
interface r2-eth0
|
||||
ip address 192.168.0.2/24
|
||||
!
|
||||
interface r2-eth1
|
||||
ip address 192.168.1.2/24
|
||||
!
|
||||
interface r2-eth2
|
||||
ip address 192.168.2.2/24
|
||||
!
|
7
tests/topotests/bfd-topo1/r3/bfdd.conf
Normal file
7
tests/topotests/bfd-topo1/r3/bfdd.conf
Normal file
@ -0,0 +1,7 @@
|
||||
bfd
|
||||
peer 192.168.1.2
|
||||
echo-interval 100
|
||||
echo-mode
|
||||
no shutdown
|
||||
!
|
||||
!
|
52
tests/topotests/bfd-topo1/r3/bgp_prefixes.json
Normal file
52
tests/topotests/bfd-topo1/r3/bgp_prefixes.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"routes": {
|
||||
"10.254.254.1/32": [
|
||||
{
|
||||
"aspath": "102 101",
|
||||
"prefix": "10.254.254.1",
|
||||
"valid": true,
|
||||
"peerId": "192.168.1.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.1.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.254.254.2/32": [
|
||||
{
|
||||
"aspath": "102",
|
||||
"prefix": "10.254.254.2",
|
||||
"valid": true,
|
||||
"peerId": "192.168.1.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.1.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.254.254.4/32": [
|
||||
{
|
||||
"aspath": "102 104",
|
||||
"prefix": "10.254.254.4",
|
||||
"valid": true,
|
||||
"peerId": "192.168.1.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.1.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
11
tests/topotests/bfd-topo1/r3/bgp_summary.json
Normal file
11
tests/topotests/bfd-topo1/r3/bgp_summary.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ipv4Unicast": {
|
||||
"as": 103,
|
||||
"peers": {
|
||||
"192.168.1.2": {
|
||||
"remoteAs": 102,
|
||||
"state": "Established"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
tests/topotests/bfd-topo1/r3/bgpd.conf
Normal file
7
tests/topotests/bfd-topo1/r3/bgpd.conf
Normal file
@ -0,0 +1,7 @@
|
||||
router bgp 103
|
||||
neighbor 192.168.1.2 remote-as 102
|
||||
neighbor 192.168.1.2 bfd
|
||||
address-family ipv4 unicast
|
||||
network 10.254.254.3/32
|
||||
exit-address-family
|
||||
!
|
6
tests/topotests/bfd-topo1/r3/peers.json
Normal file
6
tests/topotests/bfd-topo1/r3/peers.json
Normal file
@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"peer": "192.168.1.2",
|
||||
"status": "up"
|
||||
}
|
||||
]
|
3
tests/topotests/bfd-topo1/r3/zebra.conf
Normal file
3
tests/topotests/bfd-topo1/r3/zebra.conf
Normal file
@ -0,0 +1,3 @@
|
||||
interface r3-eth0
|
||||
ip address 192.168.1.1/24
|
||||
!
|
7
tests/topotests/bfd-topo1/r4/bfdd.conf
Normal file
7
tests/topotests/bfd-topo1/r4/bfdd.conf
Normal file
@ -0,0 +1,7 @@
|
||||
bfd
|
||||
peer 192.168.2.2
|
||||
transmit-interval 2000
|
||||
receive-interval 2000
|
||||
no shutdown
|
||||
!
|
||||
!
|
52
tests/topotests/bfd-topo1/r4/bgp_prefixes.json
Normal file
52
tests/topotests/bfd-topo1/r4/bgp_prefixes.json
Normal file
@ -0,0 +1,52 @@
|
||||
{
|
||||
"routes": {
|
||||
"10.254.254.1/32": [
|
||||
{
|
||||
"aspath": "102 101",
|
||||
"prefix": "10.254.254.1",
|
||||
"valid": true,
|
||||
"peerId": "192.168.2.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.2.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.254.254.2/32": [
|
||||
{
|
||||
"aspath": "102",
|
||||
"prefix": "10.254.254.2",
|
||||
"valid": true,
|
||||
"peerId": "192.168.2.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.2.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"10.254.254.3/32": [
|
||||
{
|
||||
"aspath": "102 103",
|
||||
"prefix": "10.254.254.3",
|
||||
"valid": true,
|
||||
"peerId": "192.168.2.2",
|
||||
"prefixLen": 32,
|
||||
"nexthops": [
|
||||
{
|
||||
"ip": "192.168.2.2",
|
||||
"used": true,
|
||||
"afi": "ipv4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
11
tests/topotests/bfd-topo1/r4/bgp_summary.json
Normal file
11
tests/topotests/bfd-topo1/r4/bgp_summary.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"ipv4Unicast": {
|
||||
"as": 104,
|
||||
"peers": {
|
||||
"192.168.2.2": {
|
||||
"remoteAs": 102,
|
||||
"state": "Established"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
tests/topotests/bfd-topo1/r4/bgpd.conf
Normal file
7
tests/topotests/bfd-topo1/r4/bgpd.conf
Normal file
@ -0,0 +1,7 @@
|
||||
router bgp 104
|
||||
neighbor 192.168.2.2 remote-as 102
|
||||
neighbor 192.168.2.2 bfd
|
||||
address-family ipv4 unicast
|
||||
network 10.254.254.4/32
|
||||
exit-address-family
|
||||
!
|
6
tests/topotests/bfd-topo1/r4/peers.json
Normal file
6
tests/topotests/bfd-topo1/r4/peers.json
Normal file
@ -0,0 +1,6 @@
|
||||
[
|
||||
{
|
||||
"peer": "192.168.2.2",
|
||||
"status": "up"
|
||||
}
|
||||
]
|
3
tests/topotests/bfd-topo1/r4/zebra.conf
Normal file
3
tests/topotests/bfd-topo1/r4/zebra.conf
Normal file
@ -0,0 +1,3 @@
|
||||
interface r4-eth0
|
||||
ip address 192.168.2.1/24
|
||||
!
|
73
tests/topotests/bfd-topo1/test_bfd_topo1.dot
Normal file
73
tests/topotests/bfd-topo1/test_bfd_topo1.dot
Normal file
@ -0,0 +1,73 @@
|
||||
## Color coding:
|
||||
#########################
|
||||
## Main FRR: #f08080 red
|
||||
## Switches: #d0e0d0 gray
|
||||
## RIP: #19e3d9 Cyan
|
||||
## RIPng: #fcb314 dark yellow
|
||||
## OSPFv2: #32b835 Green
|
||||
## OSPFv3: #19e3d9 Cyan
|
||||
## ISIS IPv4 #fcb314 dark yellow
|
||||
## ISIS IPv6 #9a81ec purple
|
||||
## BGP IPv4 #eee3d3 beige
|
||||
## BGP IPv6 #fdff00 yellow
|
||||
##### Colors (see http://www.color-hex.com/)
|
||||
|
||||
graph template {
|
||||
label="bfd-topo1";
|
||||
|
||||
# Routers
|
||||
r1 [
|
||||
shape=doubleoctagon,
|
||||
label="r1",
|
||||
fillcolor="#f08080",
|
||||
style=filled,
|
||||
];
|
||||
r2 [
|
||||
shape=doubleoctagon
|
||||
label="r2",
|
||||
fillcolor="#f08080",
|
||||
style=filled,
|
||||
];
|
||||
r3 [
|
||||
shape=doubleoctagon
|
||||
label="r3",
|
||||
fillcolor="#f08080",
|
||||
style=filled,
|
||||
];
|
||||
r4 [
|
||||
shape=doubleoctagon
|
||||
label="r4",
|
||||
fillcolor="#f08080",
|
||||
style=filled,
|
||||
];
|
||||
|
||||
# Switches
|
||||
sw1 [
|
||||
shape=oval,
|
||||
label="sw1\n192.168.0.0/24",
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
sw2 [
|
||||
shape=oval,
|
||||
label="sw2\n192.168.1.0/24",
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
sw3 [
|
||||
shape=oval,
|
||||
label="sw3\n192.168.2.0/24",
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
|
||||
# Connections
|
||||
r1 -- sw1 [label="eth0\n.1"];
|
||||
r2 -- sw1 [label="eth0\n.2"];
|
||||
|
||||
r3 -- sw2 [label="eth0\n.1"];
|
||||
r2 -- sw2 [label="eth1\n.2"];
|
||||
|
||||
r4 -- sw3 [label="eth0\n.1"];
|
||||
r2 -- sw3 [label="eth2\n.2"];
|
||||
}
|
BIN
tests/topotests/bfd-topo1/test_bfd_topo1.jpg
Normal file
BIN
tests/topotests/bfd-topo1/test_bfd_topo1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
247
tests/topotests/bfd-topo1/test_bfd_topo1.py
Normal file
247
tests/topotests/bfd-topo1/test_bfd_topo1.py
Normal file
@ -0,0 +1,247 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
#
|
||||
# test_bfd_topo1.py
|
||||
# Part of NetDEF Topology Tests
|
||||
#
|
||||
# Copyright (c) 2018 by
|
||||
# Network Device Education Foundation, Inc. ("NetDEF")
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software
|
||||
# for any purpose with or without fee is hereby granted, provided
|
||||
# that the above copyright notice and this permission notice appear
|
||||
# in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
|
||||
# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
||||
# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||
# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
|
||||
# OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
"""
|
||||
test_bfd_topo1.py: Test the FRR/Quagga BFD daemon.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from functools import partial
|
||||
import pytest
|
||||
|
||||
# Save the Current Working Directory to find configuration files.
|
||||
CWD = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path.append(os.path.join(CWD, '../'))
|
||||
|
||||
# pylint: disable=C0413
|
||||
# Import topogen and topotest helpers
|
||||
from lib import topotest
|
||||
from lib.topogen import Topogen, TopoRouter, get_topogen
|
||||
from lib.topolog import logger
|
||||
|
||||
# Required to instantiate the topology builder class.
|
||||
from mininet.topo import Topo
|
||||
|
||||
class BFDTopo(Topo):
|
||||
"Test topology builder"
|
||||
def build(self, *_args, **_opts):
|
||||
"Build function"
|
||||
tgen = get_topogen(self)
|
||||
|
||||
# Create 4 routers
|
||||
for routern in range(1, 5):
|
||||
tgen.add_router('r{}'.format(routern))
|
||||
|
||||
switch = tgen.add_switch('s1')
|
||||
switch.add_link(tgen.gears['r1'])
|
||||
switch.add_link(tgen.gears['r2'])
|
||||
|
||||
switch = tgen.add_switch('s2')
|
||||
switch.add_link(tgen.gears['r2'])
|
||||
switch.add_link(tgen.gears['r3'])
|
||||
|
||||
switch = tgen.add_switch('s3')
|
||||
switch.add_link(tgen.gears['r2'])
|
||||
switch.add_link(tgen.gears['r4'])
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
"Sets up the pytest environment"
|
||||
tgen = Topogen(BFDTopo, mod.__name__)
|
||||
tgen.start_topology()
|
||||
|
||||
router_list = tgen.routers()
|
||||
for rname, router in router_list.iteritems():
|
||||
router.load_config(
|
||||
TopoRouter.RD_ZEBRA,
|
||||
os.path.join(CWD, '{}/zebra.conf'.format(rname))
|
||||
)
|
||||
router.load_config(
|
||||
TopoRouter.RD_BFD,
|
||||
os.path.join(CWD, '{}/bfdd.conf'.format(rname))
|
||||
)
|
||||
router.load_config(
|
||||
TopoRouter.RD_BGP,
|
||||
os.path.join(CWD, '{}/bgpd.conf'.format(rname))
|
||||
)
|
||||
|
||||
# Initialize all routers.
|
||||
tgen.start_router()
|
||||
|
||||
# Verify that we are using the proper version and that the BFD
|
||||
# daemon exists.
|
||||
for router in router_list.values():
|
||||
# Check for Version
|
||||
if router.has_version('<', '5.1'):
|
||||
tgen.set_error('Unsupported FRR version')
|
||||
break
|
||||
|
||||
|
||||
def teardown_module(_mod):
|
||||
"Teardown the pytest environment"
|
||||
tgen = get_topogen()
|
||||
tgen.stop_topology()
|
||||
|
||||
|
||||
def test_bfd_connection():
|
||||
"Assert that the BFD peers can find themselves."
|
||||
tgen = get_topogen()
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
logger.info('waiting for bfd peers to go up')
|
||||
|
||||
for router in tgen.routers().values():
|
||||
json_file = '{}/{}/peers.json'.format(CWD, router.name)
|
||||
expected = json.loads(open(json_file).read())
|
||||
|
||||
test_func = partial(topotest.router_json_cmp,
|
||||
router, 'show bfd peers json', expected)
|
||||
_, result = topotest.run_and_expect(test_func, None, count=8, wait=0.5)
|
||||
assertmsg = '"{}" JSON output mismatches'.format(router.name)
|
||||
assert result is None, assertmsg
|
||||
|
||||
|
||||
def test_bgp_convergence():
|
||||
"Assert that BGP is converging."
|
||||
tgen = get_topogen()
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
logger.info('waiting for bgp peers to go up')
|
||||
|
||||
for router in tgen.routers().values():
|
||||
ref_file = '{}/{}/bgp_summary.json'.format(CWD, router.name)
|
||||
expected = json.loads(open(ref_file).read())
|
||||
test_func = partial(topotest.router_json_cmp,
|
||||
router, 'show ip bgp summary json', expected)
|
||||
_, res = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
|
||||
assertmsg = '{}: bgp did not converge'.format(router.name)
|
||||
assert res is None, assertmsg
|
||||
|
||||
|
||||
def test_bgp_fast_convergence():
|
||||
"Assert that BGP is converging before setting a link down."
|
||||
tgen = get_topogen()
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
logger.info('waiting for bgp peers converge')
|
||||
|
||||
for router in tgen.routers().values():
|
||||
ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name)
|
||||
expected = json.loads(open(ref_file).read())
|
||||
test_func = partial(topotest.router_json_cmp,
|
||||
router, 'show ip bgp json', expected)
|
||||
_, res = topotest.run_and_expect(test_func, None, count=40, wait=0.5)
|
||||
assertmsg = '{}: bgp did not converge'.format(router.name)
|
||||
assert res is None, assertmsg
|
||||
|
||||
|
||||
def test_bfd_fast_convergence():
|
||||
"""
|
||||
Assert that BFD notices the link down after simulating network
|
||||
failure.
|
||||
"""
|
||||
tgen = get_topogen()
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
# Disable r1-eth0 link.
|
||||
tgen.gears['r1'].link_enable('r1-eth0', enabled=False)
|
||||
|
||||
# Wait the minimum time we can before checking that BGP/BFD
|
||||
# converged.
|
||||
logger.info('waiting for BFD converge')
|
||||
|
||||
# Check that BGP converged quickly.
|
||||
for router in tgen.routers().values():
|
||||
json_file = '{}/{}/peers.json'.format(CWD, router.name)
|
||||
expected = json.loads(open(json_file).read())
|
||||
|
||||
# Load the same file as previous test, but expect R1 to be down.
|
||||
if router.name == 'r1':
|
||||
for peer in expected:
|
||||
if peer['peer'] == '192.168.0.2':
|
||||
peer['status'] = 'down'
|
||||
else:
|
||||
for peer in expected:
|
||||
if peer['peer'] == '192.168.0.1':
|
||||
peer['status'] = 'down'
|
||||
|
||||
test_func = partial(topotest.router_json_cmp,
|
||||
router, 'show bfd peers json', expected)
|
||||
_, res = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
|
||||
assertmsg = '"{}" JSON output mismatches'.format(router.name)
|
||||
assert res is None, assertmsg
|
||||
|
||||
|
||||
def test_bgp_fast_reconvergence():
|
||||
"Assert that BGP is converging after setting a link down."
|
||||
tgen = get_topogen()
|
||||
if tgen.routers_have_failure():
|
||||
pytest.skip(tgen.errors)
|
||||
|
||||
logger.info('waiting for BGP re convergence')
|
||||
|
||||
# Check that BGP converged quickly.
|
||||
for router in tgen.routers().values():
|
||||
ref_file = '{}/{}/bgp_prefixes.json'.format(CWD, router.name)
|
||||
expected = json.loads(open(ref_file).read())
|
||||
|
||||
# Load the same file as previous test, but set networks to None
|
||||
# to test absence.
|
||||
if router.name == 'r1':
|
||||
expected['routes']['10.254.254.2/32'] = None
|
||||
expected['routes']['10.254.254.3/32'] = None
|
||||
expected['routes']['10.254.254.4/32'] = None
|
||||
else:
|
||||
expected['routes']['10.254.254.1/32'] = None
|
||||
|
||||
test_func = partial(topotest.router_json_cmp,
|
||||
router, 'show ip bgp json', expected)
|
||||
_, res = topotest.run_and_expect(
|
||||
test_func,
|
||||
None,
|
||||
count=3,
|
||||
wait=1
|
||||
)
|
||||
assertmsg = '{}: bgp did not converge'.format(router.name)
|
||||
assert res is None, assertmsg
|
||||
|
||||
|
||||
def test_memory_leak():
|
||||
"Run the memory leak test and report results."
|
||||
tgen = get_topogen()
|
||||
if not tgen.is_memleak_enabled():
|
||||
pytest.skip('Memory leak test/report is disabled')
|
||||
|
||||
tgen.report_memory_leaks()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = ["-s"] + sys.argv[1:]
|
||||
sys.exit(pytest.main(args))
|
0
tests/topotests/bgp-ecmp-topo1/__init__.py
Normal file
0
tests/topotests/bgp-ecmp-topo1/__init__.py
Normal file
206
tests/topotests/bgp-ecmp-topo1/bgp-ecmp-topo1.dot
Normal file
206
tests/topotests/bgp-ecmp-topo1/bgp-ecmp-topo1.dot
Normal file
@ -0,0 +1,206 @@
|
||||
## Color coding:
|
||||
#########################
|
||||
## Main FRR: #f08080 red
|
||||
## Switches: #d0e0d0 gray
|
||||
## RIP: #19e3d9 Cyan
|
||||
## RIPng: #fcb314 dark yellow
|
||||
## OSPFv2: #32b835 Green
|
||||
## OSPFv3: #19e3d9 Cyan
|
||||
## ISIS IPv4 #fcb314 dark yellow
|
||||
## ISIS IPv6 #9a81ec purple
|
||||
## BGP IPv4 #eee3d3 beige
|
||||
## BGP IPv6 #fdff00 yellow
|
||||
##### Colors (see http://www.color-hex.com/)
|
||||
|
||||
graph ospf_ecmp_iBGP_topo1 {
|
||||
label="bgp ecmp topo1 - eBGP with different AS numbers";
|
||||
labelloc="t";
|
||||
|
||||
# Routers
|
||||
r1 [
|
||||
label="r1\nrtr-id 10.0.255.1/32",
|
||||
shape=doubleoctagon,
|
||||
fillcolor="#f08080",
|
||||
style=filled,
|
||||
];
|
||||
|
||||
# 4 Switches for eBGP Peers
|
||||
s1 [
|
||||
label="s1\n10.0.1.0/24",
|
||||
shape=oval,
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
s2 [
|
||||
label="s2\n10.0.2.0/24",
|
||||
shape=oval,
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
s3 [
|
||||
label="s3\n10.0.3.0/24",
|
||||
shape=oval,
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
s4 [
|
||||
label="s4\n10.0.4.0/24",
|
||||
shape=oval,
|
||||
fillcolor="#d0e0d0",
|
||||
style=filled,
|
||||
];
|
||||
|
||||
# 20 ExaBGP Peers AS 101...120
|
||||
peer1 [
|
||||
label="eBGP peer1\nAS99\nrtr-id 10.0.1.101/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer2 [
|
||||
label="eBGP peer2\nAS99\nrtr-id 10.0.1.102/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer3 [
|
||||
label="eBGP peer3\nAS99\nrtr-id 10.0.1.103/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer4 [
|
||||
label="eBGP peer4\nAS99\nrtr-id 10.0.1.104/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer5 [
|
||||
label="eBGP peer5\nAS99\nrtr-id 10.0.1.105/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer6 [
|
||||
label="eBGP peer6\nAS99\nrtr-id 10.0.2.106/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer7 [
|
||||
label="eBGP peer7\nAS99\nrtr-id 10.0.2.107/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer8 [
|
||||
label="eBGP peer8\nAS99\nrtr-id 10.0.2.108/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer9 [
|
||||
label="eBGP peer9\nAS99\nrtr-id 10.0.2.109/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer10 [
|
||||
label="eBGP peer10\nAS99\nrtr-id 10.0.2.110/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer11 [
|
||||
label="eBGP peer11\nAS111\nrtr-id 10.0.3.111/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer12 [
|
||||
label="eBGP peer12\nAS112\nrtr-id 10.0.3.112/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer13 [
|
||||
label="eBGP peer13\nAS113\nrtr-id 10.0.3.113/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer14 [
|
||||
label="eBGP peer14\nAS114\nrtr-id 10.0.3.114/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer15 [
|
||||
label="eBGP peer15\nAS115\nrtr-id 10.0.3.115/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer16 [
|
||||
label="eBGP peer16\nAS116\nrtr-id 10.0.4.116/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer17 [
|
||||
label="eBGP peer17\nAS117\nrtr-id 10.0.4.117/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer18 [
|
||||
label="eBGP peer18\nAS118\nrtr-id 10.0.4.118/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer19 [
|
||||
label="eBGP peer19\nAS119\nrtr-id 10.0.4.119/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
peer20 [
|
||||
label="eBGP peer20\nAS120\nrtr-id 10.0.4.120/32",
|
||||
shape=rectangle,
|
||||
fillcolor="#eee3d3",
|
||||
style=filled,
|
||||
];
|
||||
|
||||
# Connections
|
||||
r1 -- s1 [label="eth0\n.1"];
|
||||
r1 -- s2 [label="eth1\n.1"];
|
||||
r1 -- s3 [label="eth2\n.1"];
|
||||
r1 -- s4 [label="eth3\n.1"];
|
||||
|
||||
peer1 -- s1 [label="eth0\n.101"];
|
||||
peer2 -- s1 [label="eth0\n.102"];
|
||||
peer3 -- s1 [label="eth0\n.103"];
|
||||
peer4 -- s1 [label="eth0\n.104"];
|
||||
peer5 -- s1 [label="eth0\n.105"];
|
||||
peer6 -- s2 [label="eth0\n.106"];
|
||||
peer7 -- s2 [label="eth0\n.107"];
|
||||
peer8 -- s2 [label="eth0\n.108"];
|
||||
peer9 -- s2 [label="eth0\n.109"];
|
||||
peer10 -- s2 [label="eth0\n.110"];
|
||||
peer11 -- s3 [label="eth0\n.111"];
|
||||
peer12 -- s3 [label="eth0\n.112"];
|
||||
peer13 -- s3 [label="eth0\n.113"];
|
||||
peer14 -- s3 [label="eth0\n.114"];
|
||||
peer15 -- s3 [label="eth0\n.115"];
|
||||
peer16 -- s4 [label="eth0\n.116"];
|
||||
peer17 -- s4 [label="eth0\n.117"];
|
||||
peer18 -- s4 [label="eth0\n.118"];
|
||||
peer19 -- s4 [label="eth0\n.119"];
|
||||
peer20 -- s4 [label="eth0\n.120"];
|
||||
|
||||
# Arrange network to make cleaner diagram
|
||||
{ rank=same peer1 peer2 peer3 peer4 peer5 } -- s1 -- { rank=same peer6 peer7 peer8 peer9 peer10 } -- s2
|
||||
-- { rank=same peer11 peer12 peer13 peer14 peer15 } -- s3 -- { rank=same peer16 peer17 peer18 peer19 peer20 } -- s4
|
||||
-- { rank=same r1 } [style=invis]
|
||||
}
|
BIN
tests/topotests/bgp-ecmp-topo1/bgp-ecmp-topo1.pdf
Normal file
BIN
tests/topotests/bgp-ecmp-topo1/bgp-ecmp-topo1.pdf
Normal file
Binary file not shown.
54
tests/topotests/bgp-ecmp-topo1/exabgp.env
Normal file
54
tests/topotests/bgp-ecmp-topo1/exabgp.env
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
[exabgp.api]
|
||||
encoder = text
|
||||
highres = false
|
||||
respawn = false
|
||||
socket = ''
|
||||
|
||||
[exabgp.bgp]
|
||||
openwait = 60
|
||||
|
||||
[exabgp.cache]
|
||||
attributes = true
|
||||
nexthops = true
|
||||
|
||||
[exabgp.daemon]
|
||||
daemonize = true
|
||||
pid = '/var/run/exabgp/exabgp.pid'
|
||||
user = 'exabgp'
|
||||
##daemonize = false
|
||||
|
||||
[exabgp.log]
|
||||
all = false
|
||||
configuration = true
|
||||
daemon = true
|
||||
destination = '/var/log/exabgp.log'
|
||||
enable = true
|
||||
level = INFO
|
||||
message = false
|
||||
network = true
|
||||
packets = false
|
||||
parser = false
|
||||
processes = true
|
||||
reactor = true
|
||||
rib = false
|
||||
routes = false
|
||||
short = false
|
||||
timers = false
|
||||
|
||||
[exabgp.pdb]
|
||||
enable = false
|
||||
|
||||
[exabgp.profile]
|
||||
enable = false
|
||||
file = ''
|
||||
|
||||
[exabgp.reactor]
|
||||
speed = 1.0
|
||||
|
||||
[exabgp.tcp]
|
||||
acl = false
|
||||
bind = ''
|
||||
delay = 0
|
||||
once = false
|
||||
port = 179
|
38
tests/topotests/bgp-ecmp-topo1/peer1/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer1/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer1/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer1/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer1/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer1/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 1 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 1";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.1.1 {
|
||||
router-id 10.0.1.101;
|
||||
local-address 10.0.1.101;
|
||||
local-as 99;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer10/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer10/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer10/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer10/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer10/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer10/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 10 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 10";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.2.1 {
|
||||
router-id 10.0.2.110;
|
||||
local-address 10.0.2.110;
|
||||
local-as 99;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer11/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer11/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer11/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer11/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer11/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer11/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 11 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 11";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.3.1 {
|
||||
router-id 10.0.3.111;
|
||||
local-address 10.0.3.111;
|
||||
local-as 111;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer12/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer12/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer12/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer12/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer12/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer12/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 12 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 12";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.3.1 {
|
||||
router-id 10.0.3.112;
|
||||
local-address 10.0.3.112;
|
||||
local-as 112;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer13/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer13/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer13/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer13/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer13/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer13/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 13 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 13";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.3.1 {
|
||||
router-id 10.0.3.113;
|
||||
local-address 10.0.3.113;
|
||||
local-as 113;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer14/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer14/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer14/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer14/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer14/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer14/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 14 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 14";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.3.1 {
|
||||
router-id 10.0.3.114;
|
||||
local-address 10.0.3.114;
|
||||
local-as 114;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer15/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer15/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer15/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer15/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer15/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer15/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 15 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 15";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.3.1 {
|
||||
router-id 10.0.3.115;
|
||||
local-address 10.0.3.115;
|
||||
local-as 115;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer16/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer16/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer16/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer16/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer16/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer16/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 16 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 16";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.4.1 {
|
||||
router-id 10.0.4.116;
|
||||
local-address 10.0.4.116;
|
||||
local-as 116;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer17/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer17/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer17/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer17/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer17/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer17/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 17 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 17";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.4.1 {
|
||||
router-id 10.0.4.117;
|
||||
local-address 10.0.4.117;
|
||||
local-as 117;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer18/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer18/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer18/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer18/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer18/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer18/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 18 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 18";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.4.1 {
|
||||
router-id 10.0.4.118;
|
||||
local-address 10.0.4.118;
|
||||
local-as 118;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer19/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer19/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer19/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer19/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer19/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer19/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 19 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 19";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.4.1 {
|
||||
router-id 10.0.4.119;
|
||||
local-address 10.0.4.119;
|
||||
local-as 119;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer2/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer2/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
49
tests/topotests/bgp-ecmp-topo1/peer2/exa-send.py
Executable file
49
tests/topotests/bgp-ecmp-topo1/peer2/exa-send.py
Executable file
@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-send.py: Send a few testroutes with ExaBGP
|
||||
"""
|
||||
|
||||
from sys import stdout,argv
|
||||
from time import sleep
|
||||
|
||||
sleep(5)
|
||||
|
||||
# 1st arg is peer number
|
||||
# 2nd arg is number of routes to send
|
||||
peer = int(argv[1])
|
||||
numRoutes = int(argv[2])
|
||||
if (peer <= 10):
|
||||
asnum = 99
|
||||
else:
|
||||
asnum = peer+100
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n' % (i, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n' % (i, peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.flush()
|
||||
|
||||
# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
|
||||
for i in range(0, numRoutes):
|
||||
stdout.write('announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (i, peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
# Announce 2 different route per peer
|
||||
stdout.write('announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n' % (peer, (((peer-1) / 5) + 1), peer+100))
|
||||
stdout.write('announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n' % (peer, (((peer-1) / 5) + 1), peer+100, asnum))
|
||||
stdout.flush()
|
||||
|
||||
#Loop endlessly to allow ExaBGP to continue running
|
||||
while True:
|
||||
sleep(1)
|
||||
|
21
tests/topotests/bgp-ecmp-topo1/peer2/exabgp.cfg
Normal file
21
tests/topotests/bgp-ecmp-topo1/peer2/exabgp.cfg
Normal file
@ -0,0 +1,21 @@
|
||||
group controller {
|
||||
|
||||
process announce-routes {
|
||||
run "/etc/exabgp/exa-send.py 2 10";
|
||||
}
|
||||
|
||||
process receive-routes {
|
||||
run "/etc/exabgp/exa-receive.py 2";
|
||||
receive-routes;
|
||||
encoder text;
|
||||
}
|
||||
|
||||
neighbor 10.0.1.1 {
|
||||
router-id 10.0.1.102;
|
||||
local-address 10.0.1.102;
|
||||
local-as 99;
|
||||
peer-as 100;
|
||||
graceful-restart;
|
||||
}
|
||||
|
||||
}
|
38
tests/topotests/bgp-ecmp-topo1/peer20/exa-receive.py
Executable file
38
tests/topotests/bgp-ecmp-topo1/peer20/exa-receive.py
Executable file
@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
exa-receive.py: Save received routes form ExaBGP into file
|
||||
"""
|
||||
|
||||
from sys import stdin,argv
|
||||
from datetime import datetime
|
||||
|
||||
# 1st arg is peer number
|
||||
peer = int(argv[1])
|
||||
|
||||
# When the parent dies we are seeing continual newlines, so we only access so many before stopping
|
||||
counter = 0
|
||||
|
||||
routesavefile = open('/tmp/peer%s-received.log' % peer, 'w')
|
||||
|
||||
while True:
|
||||
try:
|
||||
line = stdin.readline()
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H:%M:%S - ')
|
||||
routesavefile.write(timestamp + line)
|
||||
routesavefile.flush()
|
||||
|
||||
if line == "":
|
||||
counter += 1
|
||||
if counter > 100:
|
||||
break
|
||||
continue
|
||||
|
||||
counter = 0
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
except IOError:
|
||||
# most likely a signal during readline
|
||||
pass
|
||||
|
||||
routesavefile.close()
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user