Merge pull request #3384 from donaldsharp/topotests

Topotests
This commit is contained in:
Christian Franke 2018-11-29 15:29:08 +01:00 committed by GitHub
commit f2ce6a57a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
625 changed files with 28297 additions and 0 deletions

2
tests/pytest.ini Normal file
View File

@ -0,0 +1,2 @@
[pytest]
norecursedirs = topotests

4
tests/topotests/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
.cache
__pycache__
*.pyc
.pytest_cache

View 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:
![template topology graph](example-test/test_template.jpg)
### 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
View 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
View 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
```

View 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
!

View 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
!

View 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
!

View 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
!

View 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
!

View 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
!

View 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)

View 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
!

View 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

View 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
!

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View 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
!

View File

@ -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"];
}

View 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)

View File

View File

@ -0,0 +1,6 @@
bfd
peer 192.168.0.2
echo-mode
no shutdown
!
!

View 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"
}
]
}
]
}
}

View File

@ -0,0 +1,11 @@
{
"ipv4Unicast": {
"as": 101,
"peers": {
"192.168.0.2": {
"remoteAs": 102,
"state": "Established"
}
}
}
}

View 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
!

View File

@ -0,0 +1,8 @@
[
{
"remote-receive-interval": 1000,
"remote-transmit-interval": 500,
"peer": "192.168.0.2",
"status": "up"
}
]

View File

@ -0,0 +1,3 @@
interface r1-eth0
ip address 192.168.0.1/24
!

View 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
!
!

View 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"
}
]
}
]
}
}

View 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"
}
}
}
}

View 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
!

View 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"
}
]

View 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
!

View File

@ -0,0 +1,7 @@
bfd
peer 192.168.1.2
echo-interval 100
echo-mode
no shutdown
!
!

View 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"
}
]
}
]
}
}

View File

@ -0,0 +1,11 @@
{
"ipv4Unicast": {
"as": 103,
"peers": {
"192.168.1.2": {
"remoteAs": 102,
"state": "Established"
}
}
}
}

View 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
!

View File

@ -0,0 +1,6 @@
[
{
"peer": "192.168.1.2",
"status": "up"
}
]

View File

@ -0,0 +1,3 @@
interface r3-eth0
ip address 192.168.1.1/24
!

View File

@ -0,0 +1,7 @@
bfd
peer 192.168.2.2
transmit-interval 2000
receive-interval 2000
no shutdown
!
!

View 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"
}
]
}
]
}
}

View File

@ -0,0 +1,11 @@
{
"ipv4Unicast": {
"as": 104,
"peers": {
"192.168.2.2": {
"remoteAs": 102,
"state": "Established"
}
}
}
}

View 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
!

View File

@ -0,0 +1,6 @@
[
{
"peer": "192.168.2.2",
"status": "up"
}
]

View File

@ -0,0 +1,3 @@
interface r4-eth0
ip address 192.168.2.1/24
!

View 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"];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View 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))

View 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]
}

Binary file not shown.

View 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

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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()

View 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)

View 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;
}
}

View 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