Compare commits
30 Commits
ef79717ebe
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
043d58675d | ||
|
|
dfbe5815c9 | ||
|
|
677ef6a5e3 | ||
|
|
f6afc4212c | ||
|
|
9fc41679fd | ||
|
|
a7fb7db978 | ||
|
|
d623bbd79f | ||
|
|
bb4e55c062 | ||
|
|
d8369e2c10 | ||
|
|
039d09d358 | ||
|
|
ad72dae812 | ||
|
|
77ed63e577 | ||
|
|
f116a08aa0 | ||
|
|
a5b19b3139 | ||
|
|
20ddc553e1 | ||
|
|
0a38cd20c1 | ||
|
|
f0d00fad0d | ||
|
|
e2cac9e288 | ||
|
|
ea8cd89de9 | ||
|
|
2b03aad9bc | ||
|
|
17c3977873 | ||
|
|
e5889b22e2 | ||
|
|
49b8df9709 | ||
|
|
dc1840a6ec | ||
|
|
7114b24331 | ||
|
|
4c640d7f10 | ||
|
|
b16599d267 | ||
|
|
88ee8a2ae8 | ||
|
|
647030927a | ||
|
|
659ae59a3b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,2 +1,4 @@
|
|||||||
clab-*
|
clab-*
|
||||||
**/*.bak
|
**/*.bak
|
||||||
|
tests/.venv/
|
||||||
|
tests/out/
|
||||||
|
|||||||
171
BUILDING.md
Normal file
171
BUILDING.md
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
# Building vpp-containerlab
|
||||||
|
|
||||||
|
This document describes how to build, test and release the `vpp-containerlab` Docker image.
|
||||||
|
The image is built natively on two machines and combined into a multi-arch manifest:
|
||||||
|
|
||||||
|
- `summer` — amd64, Linux (local machine)
|
||||||
|
- `jessica-orb` — arm64, OrbStack VM on macOS, reachable via `ssh jessica-orb`
|
||||||
|
|
||||||
|
The pipeline sideloads locally-built VPP `.deb` packages rather than pulling from packagecloud,
|
||||||
|
so VPP must be compiled on both machines before building the image.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### SSH access to jessica-orb
|
||||||
|
|
||||||
|
The Docker daemon on `jessica` runs inside OrbStack's Linux VM. OrbStack listens on
|
||||||
|
`127.0.0.1:32222`; add a jump-host entry to `~/.ssh/config` on `summer` to reach it:
|
||||||
|
|
||||||
|
```
|
||||||
|
Host jessica-orb
|
||||||
|
HostName 127.0.0.1
|
||||||
|
Port 32222
|
||||||
|
User pim
|
||||||
|
ProxyCommand ssh jessica -W 127.0.0.1:32222
|
||||||
|
IdentityFile ~/.ssh/jessica-orb-key
|
||||||
|
IdentitiesOnly yes
|
||||||
|
UserKnownHostsFile /dev/null
|
||||||
|
StrictHostKeyChecking no
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy OrbStack's SSH key from `jessica` to `summer`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scp jessica:~/.orbstack/ssh/id_ed25519 ~/.ssh/jessica-orb-key
|
||||||
|
chmod 600 ~/.ssh/jessica-orb-key
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify the full chain works:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh jessica-orb 'uname -m && docker info | head -3'
|
||||||
|
# expected: aarch64
|
||||||
|
```
|
||||||
|
|
||||||
|
### One-time setup
|
||||||
|
|
||||||
|
Install the Robot Framework venv for running tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make venv
|
||||||
|
```
|
||||||
|
|
||||||
|
This only needs to be re-run if `tests/requirements.txt` changes.
|
||||||
|
|
||||||
|
### Before every release
|
||||||
|
|
||||||
|
Build VPP on both machines (`make pkg-deb` in your VPP source tree on both `summer` and the
|
||||||
|
OrbStack VM on `jessica`), then verify both machines have a consistent set of `.deb` packages:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make preflight
|
||||||
|
```
|
||||||
|
|
||||||
|
This checks that `~/src/vpp/build-root` on each machine contains exactly one version of each
|
||||||
|
required package and that the version on `summer` matches the version on `jessica-orb`.
|
||||||
|
Override the path if your build root is elsewhere:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make preflight VPPDEBS=~/src/vpp/other-build-root
|
||||||
|
```
|
||||||
|
|
||||||
|
## Release pipeline
|
||||||
|
|
||||||
|
The full pipeline runs in this order:
|
||||||
|
|
||||||
|
```
|
||||||
|
preflight → build → test → push → release
|
||||||
|
```
|
||||||
|
|
||||||
|
Run everything in one shot:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make all
|
||||||
|
```
|
||||||
|
|
||||||
|
Or step through it manually:
|
||||||
|
|
||||||
|
| Step | Command | What it does |
|
||||||
|
|------|---------|--------------|
|
||||||
|
| 1 | `make preflight` | Validate VPP debs on summer and jessica-orb |
|
||||||
|
| 2 | `make build-amd64` | Build image locally for amd64 |
|
||||||
|
| 3 | `make test-amd64` | Run e2e tests against the amd64 image |
|
||||||
|
| 4 | `make sync-arm64` | Rsync working tree to jessica-orb |
|
||||||
|
| 5 | `make build-arm64` | Build image on jessica-orb for arm64 |
|
||||||
|
| 6 | `make test-arm64` | Run e2e tests on jessica-orb against the arm64 image |
|
||||||
|
| 7 | `make push-amd64` | Tag and push `:latest-amd64` to the registry |
|
||||||
|
| 8 | `make push-arm64` | Tag and push `:latest-arm64` to the registry |
|
||||||
|
| 9 | `make release` | Combine into a single `:latest` multi-arch manifest |
|
||||||
|
|
||||||
|
Convenience targets:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build # steps 2+4+5 (both platforms)
|
||||||
|
make test # steps 3+6 (both platforms)
|
||||||
|
make push # steps 7+8 (both platforms)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Promoting to :stable
|
||||||
|
|
||||||
|
`:stable` is only promoted **after** a successful `make all` — meaning both amd64 and arm64
|
||||||
|
have been built, tested, pushed and combined into `:latest`. Do not run `make stable` unless
|
||||||
|
the full pipeline completed without errors.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make all && make stable
|
||||||
|
```
|
||||||
|
|
||||||
|
`make stable` points `:stable` at the same manifest as the current `:latest-amd64` and
|
||||||
|
`:latest-arm64`, so it is always in sync with a fully tested release.
|
||||||
|
|
||||||
|
## Running a single test suite
|
||||||
|
|
||||||
|
Pass `TEST=` to restrict which suite is run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test-amd64 TEST=tests/01-vpp-bird
|
||||||
|
make test TEST=tests/02-vpp-frr
|
||||||
|
```
|
||||||
|
|
||||||
|
The default is `tests/` (all suites).
|
||||||
|
|
||||||
|
## Debugging test failures
|
||||||
|
|
||||||
|
**Read the HTML log** — written after every run regardless of outcome:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
xdg-open tests/out/tests-docker-log.html
|
||||||
|
```
|
||||||
|
|
||||||
|
**Deploy the topology manually** to keep containers running for inspection:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
IMAGE=git.ipng.ch/ipng/vpp-containerlab:latest-amd64-test \
|
||||||
|
containerlab deploy -t tests/01-vpp-bird/e2e-lab/vpp.clab.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
Then inspect live state:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enter the VPP1 container:
|
||||||
|
containerlab tools dc -t tests/01-vpp-bird/e2e-lab/vpp.clab.yml vpp1
|
||||||
|
|
||||||
|
# OSPF neighbor state
|
||||||
|
containerlab exec -t tests/01-vpp-bird/e2e-lab/vpp.clab.yml \
|
||||||
|
--label clab-node-name=vpp1 --cmd "birdc show ospf neighbor"
|
||||||
|
|
||||||
|
# Manual ping
|
||||||
|
containerlab exec -t tests/01-vpp-bird/e2e-lab/vpp.clab.yml \
|
||||||
|
--label clab-node-name=client1 --cmd "ping -c 5 10.82.98.82"
|
||||||
|
|
||||||
|
# Tear down when done
|
||||||
|
containerlab destroy -t tests/01-vpp-bird/e2e-lab/vpp.clab.yml --cleanup
|
||||||
|
```
|
||||||
|
|
||||||
|
**Common cause — OSPF convergence time:** 100% ping loss usually means routing is not up yet.
|
||||||
|
Tune the `Sleep` duration in the relevant `.robot` file by deploying manually and watching
|
||||||
|
`birdc show ospf neighbor` (or `vtysh -c "show ip ospf neighbor"` for FRR) until all
|
||||||
|
neighbors reach state `Full`.
|
||||||
|
|
||||||
|
**Increase robot verbosity:** add `--loglevel DEBUG` to the `robot` invocation in
|
||||||
|
`tests/rf-run.sh` temporarily.
|
||||||
131
Makefile
Normal file
131
Makefile
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
IMG := git.ipng.ch/ipng/vpp-containerlab
|
||||||
|
BUILDHOST := jessica-orb
|
||||||
|
BUILDDIR := ~/src/.vpp-containerlab-build
|
||||||
|
VPPDEBS := $(HOME)/src/vpp/build-root
|
||||||
|
TEST ?= tests/
|
||||||
|
|
||||||
|
.PHONY: all help preflight build build-amd64 build-arm64 sync-arm64 test test-amd64 test-arm64 \
|
||||||
|
push push-amd64 push-arm64 release stable venv
|
||||||
|
|
||||||
|
help:
|
||||||
|
@echo "vpp-containerlab build, test and release"
|
||||||
|
@echo ""
|
||||||
|
@echo "Typical workflow:"
|
||||||
|
@echo " 1. make venv Set up local Robot Framework venv (once, or after requirements change)"
|
||||||
|
@echo " 2. make preflight Verify VPP debs in VPPDEBS= on summer and jessica-orb"
|
||||||
|
@echo " 3. make build-amd64 Build image locally for amd64 (sideloading VPPDEBS)"
|
||||||
|
@echo " 4. make test-amd64 Run e2e tests locally against the amd64 image"
|
||||||
|
@echo " 5. make sync-arm64 Rsync working tree to jessica-orb and set up venv there"
|
||||||
|
@echo " 6. make build-arm64 Build image on jessica-orb for arm64 (sideloading VPPDEBS)"
|
||||||
|
@echo " 7. make test-arm64 Run e2e tests on jessica-orb against the arm64 image"
|
||||||
|
@echo " 8. make push-amd64 Tag and push :latest-amd64 to the registry"
|
||||||
|
@echo " 9. make push-arm64 Tag and push :latest-arm64 to the registry (runs on jessica-orb)"
|
||||||
|
@echo " 10. make release Combine into a single :latest multi-arch manifest"
|
||||||
|
@echo ""
|
||||||
|
@echo " make all Run steps 2-10 in one go (venv must already exist)"
|
||||||
|
@echo " make build Run steps 3+5+6 (sync-arm64 + both builds)"
|
||||||
|
@echo " make test Run steps 4+7 (both test suites)"
|
||||||
|
@echo " make push Run steps 8+9 (both pushes)"
|
||||||
|
@echo " make release Run step 10 (publish into a multi-arch manifest)"
|
||||||
|
@echo " make stable When all tests pass on amd64+arm64, push this ':latest' as :stable'"
|
||||||
|
@echo ""
|
||||||
|
@echo "Variables (override on command line):"
|
||||||
|
@echo " VPPDEBS=~/src/vpp/build-root Directory of locally-built VPP .deb packages"
|
||||||
|
@echo " TEST=tests/02-vpp-frr Run only a specific test suite (default: all)"
|
||||||
|
|
||||||
|
# Build both platforms, test both, push both, then combine into :latest.
|
||||||
|
all: preflight build test push release
|
||||||
|
|
||||||
|
build: build-amd64 sync-arm64 build-arm64
|
||||||
|
|
||||||
|
test: test-amd64 test-arm64
|
||||||
|
|
||||||
|
push: push-amd64 push-arm64
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Preflight — validate VPP debs on summer and jessica-orb
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Check locally, then pipe the same script to jessica-orb over SSH,
|
||||||
|
# passing the local version so jessica-orb can assert both machines match.
|
||||||
|
preflight:
|
||||||
|
python3 scripts/check-vppdebs.py $(VPPDEBS)
|
||||||
|
$(eval VPP_VERSION := $(shell python3 scripts/check-vppdebs.py --print-version $(VPPDEBS)))
|
||||||
|
ssh $(BUILDHOST) python3 - --assert-version $(VPP_VERSION) $(VPPDEBS) < scripts/check-vppdebs.py
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Local venv (summer)
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
tests/.venv: tests/requirements.txt
|
||||||
|
python3 -m venv tests/.venv
|
||||||
|
tests/.venv/bin/pip install -q -r tests/requirements.txt
|
||||||
|
|
||||||
|
venv: tests/.venv
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# amd64 — runs locally on summer
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
build-amd64:
|
||||||
|
docker buildx build --no-cache --load --platform linux/amd64 \
|
||||||
|
--build-context vppdebs=$(VPPDEBS) \
|
||||||
|
--tag $(IMG):latest-amd64-test \
|
||||||
|
-f docker/Dockerfile docker/
|
||||||
|
|
||||||
|
test-amd64: tests/.venv
|
||||||
|
IMAGE=$(IMG):latest-amd64-test tests/rf-run.sh docker $(TEST)
|
||||||
|
|
||||||
|
push-amd64:
|
||||||
|
docker tag $(IMG):latest-amd64-test $(IMG):latest-amd64
|
||||||
|
docker push $(IMG):latest-amd64
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# arm64 — runs on jessica-orb via rsync + SSH
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Wipe and re-sync summer's working tree to a stable directory on jessica-orb,
|
||||||
|
# then set up the robot venv there.
|
||||||
|
sync-arm64:
|
||||||
|
@case "$(BUILDDIR)" in \
|
||||||
|
.*|/*) echo "ERROR: BUILDDIR '$(BUILDDIR)' must not start with '.' or '/'" >&2; exit 1;; \
|
||||||
|
esac
|
||||||
|
ssh $(BUILDHOST) "rm -rf $(BUILDDIR) && mkdir -p $(BUILDDIR)"
|
||||||
|
rsync -a --exclude='.git' --exclude='tests/.venv' --exclude='tests/out' \
|
||||||
|
./ $(BUILDHOST):$(BUILDDIR)/
|
||||||
|
ssh $(BUILDHOST) "cd $(BUILDDIR) && \
|
||||||
|
python3 -m venv tests/.venv && \
|
||||||
|
tests/.venv/bin/pip install -q -r tests/requirements.txt"
|
||||||
|
|
||||||
|
build-arm64:
|
||||||
|
ssh $(BUILDHOST) "cd $(BUILDDIR) && \
|
||||||
|
docker buildx build --no-cache --load --platform linux/arm64 \
|
||||||
|
--build-context vppdebs=$(VPPDEBS) \
|
||||||
|
--tag $(IMG):latest-arm64-test \
|
||||||
|
-f docker/Dockerfile docker/"
|
||||||
|
|
||||||
|
test-arm64:
|
||||||
|
ssh $(BUILDHOST) "cd $(BUILDDIR) && \
|
||||||
|
IMAGE=$(IMG):latest-arm64-test \
|
||||||
|
tests/rf-run.sh docker $(TEST)"
|
||||||
|
|
||||||
|
push-arm64:
|
||||||
|
ssh $(BUILDHOST) "docker tag $(IMG):latest-arm64-test $(IMG):latest-arm64 && \
|
||||||
|
docker push $(IMG):latest-arm64"
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Release — combine amd64 + arm64 into a single :latest manifest
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
release:
|
||||||
|
docker buildx imagetools create \
|
||||||
|
--tag $(IMG):latest \
|
||||||
|
$(IMG):latest-amd64 \
|
||||||
|
$(IMG):latest-arm64
|
||||||
|
|
||||||
|
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
# Stable — mark latest release as stable. Only do this if all tests pass
|
||||||
|
# -------------------------------------------------------------------------
|
||||||
|
stable:
|
||||||
|
docker buildx imagetools create \
|
||||||
|
--tag $(IMG):stable \
|
||||||
|
$(IMG):latest-amd64 \
|
||||||
|
$(IMG):latest-arm64
|
||||||
152
README.md
152
README.md
@@ -1,103 +1,79 @@
|
|||||||
# VPP Containerlab Docker image
|
# VPP Containerlab Docker image
|
||||||
|
|
||||||
This docker container creates a VPP instance based on the latest VPP release. It starts up as per
|
## User Documentation
|
||||||
normal, using /etc/vpp/startup.conf (which Containerlab might replace when it starts its
|
|
||||||
containers). Once started, it'll execute `/etc/vpp/bootstrap.vpp` within the dataplane. There are
|
|
||||||
two relevant files:
|
|
||||||
|
|
||||||
1. `clab.vpp` -- generated by `files/init-container.sh`. Its purpose is to bind the `veth`
|
The file `vpp.clab.yml` contains an example topology existing of two VPP instances connected each to
|
||||||
interfaces that containerlab has added to the container into the VPP dataplane (see below).
|
one Alpine linux container, in the following topology:
|
||||||
1. `vppcfg.vpp` -- generated by `files/init-container.sh`. Its purpose is to read the user
|
|
||||||
specified `vppcfg.yaml` file and convert it into VPP CLI commands. If no YAML file is
|
|
||||||
specified, or if it is not syntactically valid, an empty file is generated instead.
|
|
||||||
|
|
||||||
For Containerlab users who wish to have more control over their VPP bootstrap, it's possible to
|

|
||||||
bind-mount `/etc/vpp/bootstrap.vpp`.
|
|
||||||
|
|
||||||
## Building
|
This container ships with both Bird2 and FRRouting as controlplane agents.
|
||||||
|
|
||||||
|
You can deploy:
|
||||||
|
* Bird2: `containerlab deploy --topo vpp-bird.clab.yml`.
|
||||||
|
* FRR: `containerlab deploy --topo vpp-frr.clab.yml`.
|
||||||
|
|
||||||
|
three relevant files for VPP are included in this repository:
|
||||||
|
1. `config/vpp*/vppcfg.yaml` configures the dataplane interfaces, including a loopback address.
|
||||||
|
1. `config/vpp*/bird-local.conf` configures the controlplane to enable BFD and OSPF.
|
||||||
|
1. `config/vpp*/frr.conf` configures the controlplane to enable BFD and OSPF.
|
||||||
|
|
||||||
|
Once the lab comes up, you can SSH to the VPP containers (`vpp1` and `vpp2`) which will have your
|
||||||
|
SSH keys installed (if available). Otherwise, you can log in as user `root` using password `vpp`.
|
||||||
|
|
||||||
|
VPP runs its own network namespace called `dataplane`, which is very similar to SR Linux default
|
||||||
|
`network-instance`. You can join it to take a look:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
IMG=git.ipng.ch/ipng/vpp-containerlab
|
pim@summer:~/src/vpp-containerlab$ ssh root@vpp1
|
||||||
TAG=latest
|
root@vpp1:~# nsenter --net=/var/run/netns/dataplane
|
||||||
docker build --no-cache -f docker/Dockerfile.bookworm -t $IMG docker/
|
root@vpp1:~# ip -br a
|
||||||
docker image tag $IMG $IMG:$TAG
|
lo DOWN
|
||||||
docker push $IMG
|
loop0 UP 10.82.98.0/32 2001:db8:8298::/128 fe80::dcad:ff:fe00:0/64
|
||||||
docker push $IMG:$TAG
|
eth1 UNKNOWN 10.82.98.65/28 2001:db8:8298:101::1/64 fe80::a8c1:abff:fe77:acb9/64
|
||||||
|
eth2 UNKNOWN 10.82.98.16/31 2001:db8:8298:1::1/64 fe80::a8c1:abff:fef0:7125/64
|
||||||
|
|
||||||
|
root@vpp1:~# ping 10.82.98.1 ## The vpp2 IPv4 loopback address
|
||||||
|
PING 10.82.98.1 (10.82.98.1) 56(84) bytes of data.
|
||||||
|
64 bytes from 10.82.98.1: icmp_seq=1 ttl=64 time=9.53 ms
|
||||||
|
64 bytes from 10.82.98.1: icmp_seq=2 ttl=64 time=15.9 ms
|
||||||
|
^C
|
||||||
|
--- 10.82.98.1 ping statistics ---
|
||||||
|
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
|
||||||
|
rtt min/avg/max/mdev = 9.530/12.735/15.941/3.205 ms
|
||||||
```
|
```
|
||||||
|
|
||||||
## Testing the container standalone
|
The two clients are running a minimalistic Alpine Linux container, which doesn't ship with SSH by
|
||||||
|
default. You can enter the containers as following:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker network create --driver=bridge clab-network --subnet=192.0.2.0/24 \
|
pim@summer:~/src/vpp-containerlab$ docker exec -it client1 sh
|
||||||
--ipv6 --subnet=2001:db8::/64
|
/ # ip addr show dev eth1
|
||||||
docker rm clab-pim
|
531235: eth1@if531234: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 9500 qdisc noqueue state UP
|
||||||
docker run --cap-add=NET_ADMIN --cap-add=SYS_NICE --cap-add=SYS_PTRACE \
|
link/ether 00:c1:ab:00:00:01 brd ff:ff:ff:ff:ff:ff
|
||||||
--device=/dev/net/tun:/dev/net/tun \
|
inet 10.82.98.66/28 scope global eth1
|
||||||
--device=/dev/vhost-net:/dev/vhost-net \
|
valid_lft forever preferred_lft forever
|
||||||
--privileged --name clab-pim \
|
inet6 2001:db8:8298:101::2/64 scope global
|
||||||
docker.io/pimvanpelt/vpp-containerlab:latest
|
valid_lft forever preferred_lft forever
|
||||||
docker network connect clab-network clab-pim
|
inet6 fe80::2c1:abff:fe00:1/64 scope link
|
||||||
|
valid_lft forever preferred_lft forever
|
||||||
|
/ # traceroute 10.82.98.82
|
||||||
|
traceroute to 10.82.98.82 (10.82.98.82), 30 hops max, 46 byte packets
|
||||||
|
1 10.82.98.65 (10.82.98.65) 5.906 ms 7.086 ms 7.868 ms
|
||||||
|
2 10.82.98.17 (10.82.98.17) 24.007 ms 23.349 ms 15.933 ms
|
||||||
|
3 10.82.98.82 (10.82.98.82) 39.978 ms 31.127 ms 31.854 ms
|
||||||
|
|
||||||
|
/ # traceroute 2001:db8:8298:102::2
|
||||||
|
traceroute to 2001:db8:8298:102::2 (2001:db8:8298:102::2), 30 hops max, 72 byte packets
|
||||||
|
1 2001:db8:8298:101::1 (2001:db8:8298:101::1) 0.701 ms 7.144 ms 7.900 ms
|
||||||
|
2 2001:db8:8298:1::2 (2001:db8:8298:1::2) 23.909 ms 22.943 ms 23.893 ms
|
||||||
|
3 2001:db8:8298:102::2 (2001:db8:8298:102::2) 31.964 ms 30.814 ms 32.000 ms
|
||||||
```
|
```
|
||||||
|
|
||||||
### A note on DPDK
|
From the vantage point of `client1`, the first hop represents the `vpp1` node, which forwards to
|
||||||
|
`vpp2`, which finally forwards to `client2`.
|
||||||
|
|
||||||
DPDK will be disabled by default as it requires hugepages and VFIO and/or UIO to use physical
|
## Developer Documentation
|
||||||
network cards. If DPDK at some future point is desired, mapping VFIO can be done by adding this:
|
|
||||||
```
|
|
||||||
--device=/dev/vfio/vfio:/dev/vfio/vfio
|
|
||||||
```
|
|
||||||
|
|
||||||
or in Containerlab, using the `devices` feature:
|
See [BUILDING.md](BUILDING.md) for instructions on building the image, sideloading locally built
|
||||||
|
VPP packages, multiarch builds, testing, and configuring VPP.
|
||||||
```
|
|
||||||
my-node:
|
|
||||||
image: vpp-containerlab:latest
|
|
||||||
kind: vpp
|
|
||||||
devices:
|
|
||||||
- /dev/vfio/vfio
|
|
||||||
- /dev/net/tun
|
|
||||||
- /dev/vhost-net
|
|
||||||
```
|
|
||||||
|
|
||||||
If using DPDK in a container, one of the userspace IO kernel drivers must be loaded in the host
|
|
||||||
kernel. Options are `igb_uio`, `vfio_pci`, or `uio_pci_generic`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ sudo modprobe igb_uio
|
|
||||||
$ sudo modprobe vfio_pci
|
|
||||||
$ sudo modprobe uio_pci_generic
|
|
||||||
```
|
|
||||||
|
|
||||||
Particularly the VFIO driver needs to be present before one can attempt to bindmount
|
|
||||||
`/dev/vfio/vfio` into the container!
|
|
||||||
|
|
||||||
## Configuring VPP
|
|
||||||
|
|
||||||
When Containerlab starts the docker containers, it'll offer one or more `veth` point to point
|
|
||||||
network links, which will show up as `eth1` and further. `eth0` is the default NIC that belongs to
|
|
||||||
the management plane in Containerlab (the one which you'll see with `containerlab inspect`). Before
|
|
||||||
VPP can use these `veth` interfaces, it needs to bind them, like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
docker exec -it clab-pim vppctl
|
|
||||||
```
|
|
||||||
|
|
||||||
and then within the VPP control shell:
|
|
||||||
|
|
||||||
```
|
|
||||||
create host-interface v2 name eth1
|
|
||||||
set interface name host-eth1 eth1
|
|
||||||
set interface mtu 1500 eth1
|
|
||||||
set interface ip address eth1 192.0.2.2/24
|
|
||||||
set interface ip address eth1 2001:db8::2/64
|
|
||||||
set interface state eth1 up
|
|
||||||
```
|
|
||||||
|
|
||||||
Containerlab will attach these `veth` pairs to the container, and replace our Docker CMD with one
|
|
||||||
that waits for all of these interfaces to be added (typically called `if-wait.sh`). In our own CMD,
|
|
||||||
we then generate a config file called `/etc/vpp/clab.vpp` which contains the necessary VPP commands
|
|
||||||
to take control over these `veth` pairs.
|
|
||||||
|
|
||||||
In addition, you can add more commands that'll execute on startup by copying in
|
|
||||||
`/etc/vpp/manual-pre.vpp` (to be executed _before_ the containerlab stuff) or
|
|
||||||
`/etc/vpp/manual-post.vpp` (to be executed _after_ the containerlab stuff).
|
|
||||||
|
|||||||
2
config/lab-frr.env
Normal file
2
config/lab-frr.env
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
BIRD_ENABLED=false
|
||||||
|
FRR_ENABLED=true
|
||||||
31
config/vpp1/frr.conf
Normal file
31
config/vpp1/frr.conf
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
frr version 10.3
|
||||||
|
frr defaults traditional
|
||||||
|
hostname vpp1
|
||||||
|
log syslog informational
|
||||||
|
service integrated-vtysh-config
|
||||||
|
!
|
||||||
|
ip router-id 10.82.98.0
|
||||||
|
!
|
||||||
|
interface eth2
|
||||||
|
ip ospf area 0
|
||||||
|
ip ospf bfd
|
||||||
|
ip ospf cost 10
|
||||||
|
ip ospf network point-to-point
|
||||||
|
ipv6 ospf6 area 0
|
||||||
|
ipv6 ospf6 bfd
|
||||||
|
ipv6 ospf6 cost 10
|
||||||
|
ipv6 ospf6 network point-to-point
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
interface loop0
|
||||||
|
ip ospf passive
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
router ospf
|
||||||
|
redistribute connected
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
router ospf6
|
||||||
|
redistribute connected
|
||||||
|
exit
|
||||||
|
!
|
||||||
@@ -6,7 +6,7 @@ interfaces:
|
|||||||
addresses: [ 10.82.98.65/28, 2001:db8:8298:101::1/64 ]
|
addresses: [ 10.82.98.65/28, 2001:db8:8298:101::1/64 ]
|
||||||
eth2:
|
eth2:
|
||||||
description: 'To vpp2'
|
description: 'To vpp2'
|
||||||
mtu: 9000
|
mtu: 9216
|
||||||
lcp: eth2
|
lcp: eth2
|
||||||
addresses: [ 10.82.98.16/31, 2001:db8:8298:1::1/64 ]
|
addresses: [ 10.82.98.16/31, 2001:db8:8298:1::1/64 ]
|
||||||
loopbacks:
|
loopbacks:
|
||||||
|
|||||||
31
config/vpp2/frr.conf
Normal file
31
config/vpp2/frr.conf
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
frr version 10.3
|
||||||
|
frr defaults traditional
|
||||||
|
hostname vpp2
|
||||||
|
log syslog informational
|
||||||
|
service integrated-vtysh-config
|
||||||
|
!
|
||||||
|
ip router-id 10.82.98.1
|
||||||
|
!
|
||||||
|
interface eth2
|
||||||
|
ip ospf area 0
|
||||||
|
ip ospf bfd
|
||||||
|
ip ospf cost 10
|
||||||
|
ip ospf network point-to-point
|
||||||
|
ipv6 ospf6 area 0
|
||||||
|
ipv6 ospf6 bfd
|
||||||
|
ipv6 ospf6 cost 10
|
||||||
|
ipv6 ospf6 network point-to-point
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
interface loop0
|
||||||
|
ip ospf passive
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
router ospf
|
||||||
|
redistribute connected
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
router ospf6
|
||||||
|
redistribute connected
|
||||||
|
exit
|
||||||
|
!
|
||||||
@@ -6,7 +6,7 @@ interfaces:
|
|||||||
addresses: [ 10.82.98.81/28, 2001:db8:8298:102::1/64 ]
|
addresses: [ 10.82.98.81/28, 2001:db8:8298:102::1/64 ]
|
||||||
eth2:
|
eth2:
|
||||||
description: 'To vpp1'
|
description: 'To vpp1'
|
||||||
mtu: 9000
|
mtu: 9216
|
||||||
lcp: eth2
|
lcp: eth2
|
||||||
addresses: [ 10.82.98.17/31, 2001:db8:8298:1::2/64 ]
|
addresses: [ 10.82.98.17/31, 2001:db8:8298:1::2/64 ]
|
||||||
loopbacks:
|
loopbacks:
|
||||||
|
|||||||
52
docker/Dockerfile
Normal file
52
docker/Dockerfile
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
# Default empty stage for local VPP debs. Override at build time with:
|
||||||
|
# --build-context vppdebs=/path/to/debs (e.g. ~/src/vpp/build-root/)
|
||||||
|
# If not overridden, falls back to installing VPP from packagecloud (ARG REPO).
|
||||||
|
FROM scratch AS vppdebs
|
||||||
|
|
||||||
|
FROM ubuntu:noble
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
ARG VPP_INSTALL_SKIP_SYSCTL=true
|
||||||
|
ARG REPO=release
|
||||||
|
EXPOSE 22/tcp
|
||||||
|
RUN apt-get update && apt-get -y install curl procps tcpdump iproute2 iptables \
|
||||||
|
iputils-ping net-tools git python3 python3-pip vim-tiny nano joe openssh-server \
|
||||||
|
mtr-tiny traceroute rsync && apt-get clean
|
||||||
|
|
||||||
|
# Install VPP - sideload from local debs if --build-context vppdebs=<path> is provided,
|
||||||
|
# otherwise install from packagecloud. Debs are bind-mounted and never stored in a layer.
|
||||||
|
RUN --mount=type=bind,from=vppdebs,target=/tmp/vpp-debs \
|
||||||
|
mkdir -p /var/log/vpp /root/.ssh/ && \
|
||||||
|
if ls /tmp/vpp-debs/vpp_*.deb 1>/dev/null 2>&1; then \
|
||||||
|
apt-get -y install /tmp/vpp-debs/libvppinfra_*.deb \
|
||||||
|
/tmp/vpp-debs/python3-vpp-api_*.deb \
|
||||||
|
/tmp/vpp-debs/vpp_*.deb \
|
||||||
|
/tmp/vpp-debs/vpp-crypto-engines_*.deb \
|
||||||
|
/tmp/vpp-debs/vpp-plugin-core_*.deb; \
|
||||||
|
else \
|
||||||
|
curl -s https://packagecloud.io/install/repositories/fdio/${REPO}/script.deb.sh | bash && \
|
||||||
|
apt-get -y install vpp vpp-plugin-core; \
|
||||||
|
fi && \
|
||||||
|
apt-get clean
|
||||||
|
|
||||||
|
# Build vppcfg
|
||||||
|
RUN pip install --break-system-packages build netaddr yamale argparse pyyaml ipaddress && \
|
||||||
|
git clone https://git.ipng.ch/ipng/vppcfg.git && cd vppcfg && python3 -m build && \
|
||||||
|
pip install --break-system-packages dist/vppcfg-*-py3-none-any.whl
|
||||||
|
|
||||||
|
# Install FRR
|
||||||
|
RUN curl -s -o /usr/share/keyrings/frrouting.gpg https://deb.frrouting.org/frr/keys.gpg && \
|
||||||
|
echo deb '[signed-by=/usr/share/keyrings/frrouting.gpg]' https://deb.frrouting.org/frr noble frr-stable \
|
||||||
|
> /etc/apt/sources.list.d/frr.list && \
|
||||||
|
apt -y update && apt -y install frr frr-pythontools && apt clean
|
||||||
|
|
||||||
|
# Install Bird2
|
||||||
|
RUN curl -s -o /usr/share/keyrings/cznic-labs-pkg.gpg https://pkg.labs.nic.cz/gpg && \
|
||||||
|
echo "deb [signed-by=/usr/share/keyrings/cznic-labs-pkg.gpg] https://pkg.labs.nic.cz/bird2 noble main" \
|
||||||
|
> /etc/apt/sources.list.d/cznic-labs-bird2.list && \
|
||||||
|
apt -y update && apt -y install bird2 && apt clean
|
||||||
|
|
||||||
|
# Config files
|
||||||
|
COPY files/etc/ /etc/
|
||||||
|
COPY files/init-container.sh /sbin/
|
||||||
|
RUN chmod 755 /sbin/init-container.sh
|
||||||
|
CMD ["/sbin/init-container.sh"]
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
FROM debian:bookworm
|
|
||||||
ARG DEBIAN_FRONTEND=noninteractive
|
|
||||||
ARG VPP_INSTALL_SKIP_SYSCTL=true
|
|
||||||
ARG REPO=release
|
|
||||||
EXPOSE 22/tcp
|
|
||||||
RUN apt-get update && apt-get -y install curl procps tcpdump iproute2 iptables \
|
|
||||||
iputils-ping net-tools git python3 python3-pip vim-tiny openssh-server bird2 \
|
|
||||||
mtr-tiny traceroute && apt-get clean
|
|
||||||
|
|
||||||
# Install VPP
|
|
||||||
RUN mkdir -p /var/log/vpp /root/.ssh/
|
|
||||||
RUN curl -s https://packagecloud.io/install/repositories/fdio/${REPO}/script.deb.sh | bash
|
|
||||||
RUN apt-get update && apt-get -y install vpp vpp-plugin-core && apt-get clean
|
|
||||||
|
|
||||||
# Build vppcfg
|
|
||||||
RUN pip install --break-system-packages build netaddr yamale argparse pyyaml ipaddress
|
|
||||||
RUN git clone https://github.com/pimvanpelt/vppcfg.git && cd vppcfg && python3 -m build && \
|
|
||||||
pip install --break-system-packages dist/vppcfg-*-py3-none-any.whl
|
|
||||||
|
|
||||||
# Config files
|
|
||||||
COPY files/etc/vpp/* /etc/vpp/
|
|
||||||
COPY files/etc/bird/* /etc/bird/
|
|
||||||
COPY files/init-container.sh /sbin/
|
|
||||||
RUN chmod 755 /sbin/init-container.sh
|
|
||||||
CMD ["/sbin/init-container.sh"]
|
|
||||||
17
docker/files/etc/frr/daemons
Normal file
17
docker/files/etc/frr/daemons
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# These are the daemons that FRR will use for VPP Containerlab
|
||||||
|
# NOTE: we need to run in the 'dataplane' network namespace, and use the `dplane_fpm_nl` plugin
|
||||||
|
bgpd=yes
|
||||||
|
ospfd=yes
|
||||||
|
ospf6d=yes
|
||||||
|
bfdd=yes
|
||||||
|
ldpd=yes
|
||||||
|
|
||||||
|
vtysh_enable=yes
|
||||||
|
watchfrr_options="--netns=dataplane"
|
||||||
|
zebra_options=" -A 127.0.0.1 -s 67108864 -M dplane_fpm_nl"
|
||||||
|
bgpd_options=" -A 127.0.0.1"
|
||||||
|
ospfd_options=" -A 127.0.0.1"
|
||||||
|
ospf6d_options=" -A ::1"
|
||||||
|
staticd_options="-A 127.0.0.1"
|
||||||
|
bfdd_options=" -A 127.0.0.1"
|
||||||
|
ldpd_options=" -A 127.0.0.1"
|
||||||
10
docker/files/etc/frr/frr.conf
Normal file
10
docker/files/etc/frr/frr.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# This is the VPP Containerlab default FRR configuration.
|
||||||
|
|
||||||
|
frr defaults traditional
|
||||||
|
log syslog informational
|
||||||
|
ip forwarding
|
||||||
|
ipv6 forwarding
|
||||||
|
service integrated-vtysh-config
|
||||||
|
!
|
||||||
|
ip router-id 192.0.2.1
|
||||||
|
!
|
||||||
@@ -1,2 +1,13 @@
|
|||||||
exec /etc/vpp/clab.vpp
|
mpls table add 0
|
||||||
exec /etc/vpp/vppcfg.vpp
|
set ip neighbor-config ip4 age 900
|
||||||
|
set ip neighbor-config ip6 age 900
|
||||||
|
lcp default netns dataplane
|
||||||
|
lcp lcp-sync on
|
||||||
|
lcp lcp-sync-unnumbered on
|
||||||
|
lcp param del-static-on-link-down on
|
||||||
|
lcp param del-dynamic-on-link-down on
|
||||||
|
lcp lcp-auto-subint off
|
||||||
|
exec /config/vpp/config/manual-pre.vpp
|
||||||
|
exec /config/vpp/config/clab.vpp
|
||||||
|
exec /config/vpp/config/vppcfg.vpp
|
||||||
|
exec /config/vpp/config/manual-post.vpp
|
||||||
|
|||||||
1
docker/files/etc/vpp/config/manual-post.vpp
Normal file
1
docker/files/etc/vpp/config/manual-post.vpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
comment { You can add commands here that will execute after vppcfg.vpp }
|
||||||
1
docker/files/etc/vpp/config/manual-pre.vpp
Normal file
1
docker/files/etc/vpp/config/manual-pre.vpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
comment { You can add commands here that will execute before clab.vpp }
|
||||||
1
docker/files/etc/vpp/config/vppcfg.vpp
Normal file
1
docker/files/etc/vpp/config/vppcfg.vpp
Normal file
@@ -0,0 +1 @@
|
|||||||
|
comment { This file will be overwritten / generated by containerlab upon deploy }
|
||||||
@@ -9,7 +9,7 @@ unix {
|
|||||||
cli-prompt vpp-clab#
|
cli-prompt vpp-clab#
|
||||||
cli-no-pager
|
cli-no-pager
|
||||||
poll-sleep-usec 100
|
poll-sleep-usec 100
|
||||||
exec /etc/vpp/bootstrap.vpp
|
exec /config/vpp/bootstrap.vpp
|
||||||
}
|
}
|
||||||
|
|
||||||
api-trace {
|
api-trace {
|
||||||
@@ -34,11 +34,16 @@ statseg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
plugin default { enable }
|
plugin default { disable }
|
||||||
plugin dpdk_plugin.so { disable }
|
plugin acl_plugin.so { enable }
|
||||||
|
plugin geneve_plugin.so { enable }
|
||||||
|
plugin gre_plugin.so { enable }
|
||||||
|
plugin ipip_plugin.so { enable }
|
||||||
plugin linux_cp_plugin.so { enable }
|
plugin linux_cp_plugin.so { enable }
|
||||||
plugin linux_nl_plugin.so { enable }
|
plugin linux_nl_plugin.so { enable }
|
||||||
plugin sflow_plugin.so { enable }
|
plugin sflow_plugin.so { enable }
|
||||||
|
plugin tap_plugin.so { enable }
|
||||||
|
plugin vxlan_plugin.so { enable }
|
||||||
}
|
}
|
||||||
|
|
||||||
linux-cp {
|
linux-cp {
|
||||||
|
|||||||
@@ -1,28 +1,61 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
STARTUP_CONFIG=${STARTUP_CONFIG:="/etc/vpp/startup.conf"}
|
STARTUP_CONFIG=${STARTUP_CONFIG:="/config/vpp/startup.conf"}
|
||||||
CLAB_VPP_FILE=${CLAB_VPP_FILE:=/etc/vpp/clab.vpp}
|
VPPCFG_YAML_FILE=${VPPCFG_YAML_FILE:="/config/vpp/vppcfg.yaml"}
|
||||||
VPPCFG_VPP_FILE=${VPPCFG_VPP_FILE:=/etc/vpp/vppcfg.vpp}
|
VPPCFG_VPP_FILE=${VPPCFG_VPP_FILE:="/config/vpp/config/vppcfg.vpp"}
|
||||||
|
CLAB_VPP_FILE=${CLAB_VPP_FILE:="/config/vpp/config/clab.vpp"}
|
||||||
NETNS=${NETNS:="dataplane"}
|
NETNS=${NETNS:="dataplane"}
|
||||||
BIRD_ENABLED=${BIRD_ENABLED:="true"}
|
BIRD_ENABLED=${BIRD_ENABLED:="true"}
|
||||||
|
BIRD_CONFIG=${BIRD_CONFIG:="/config/bird/bird.conf"}
|
||||||
|
FRR_ENABLED=${FRR_ENABLED:="false"}
|
||||||
|
FRR_CONFIG=${FRR_CONFIG:="/config/frr/frr.conf"}
|
||||||
|
|
||||||
echo "Creating dataplane namespace"
|
echo "Creating dataplane namespace"
|
||||||
/usr/bin/mkdir -p /etc/netns/$NETNS
|
/usr/bin/mkdir -p /etc/netns/$NETNS
|
||||||
/usr/bin/touch /etc/netns/$NETNS/resolv.conf
|
/usr/bin/touch /etc/netns/$NETNS/resolv.conf
|
||||||
/usr/sbin/ip netns add $NETNS
|
/usr/sbin/ip netns add $NETNS
|
||||||
|
/usr/bin/nsenter --net=/run/netns/$NETNS /usr/sbin/ip link set lo up
|
||||||
|
|
||||||
echo "Starting SSH, with credentials root:vpp"
|
echo "Starting SSH, with credentials root:vpp"
|
||||||
sed -i -e 's,^#PermitRootLogin prohibit-password,PermitRootLogin yes,' /etc/ssh/sshd_config
|
sed -i -e 's,^#PermitRootLogin prohibit-password,PermitRootLogin yes,' /etc/ssh/sshd_config
|
||||||
sed -i -e 's,^root:.*,root:$y$j9T$kG8pyZEVmwLXEtXekQCRK.$9iJxq/bEx5buni1hrC8VmvkDHRy7ZMsw9wYvwrzexID:20211::::::,' /etc/shadow
|
sed -i -e 's,^root:.*,root:$y$j9T$kG8pyZEVmwLXEtXekQCRK.$9iJxq/bEx5buni1hrC8VmvkDHRy7ZMsw9wYvwrzexID:20211::::::,' /etc/shadow
|
||||||
/etc/init.d/ssh start
|
/etc/init.d/ssh start
|
||||||
|
|
||||||
|
# TODO(pim): Remove this after containerlab 0.75 is released
|
||||||
|
if [ ! -r /config/vpp/startup.conf ]; then
|
||||||
|
echo "Detected old containerlab binary - invoking backwards compatible behavior"
|
||||||
|
STARTUP_CONFIG="/etc/vpp/startup.conf"
|
||||||
|
VPPCFG_YAML_FILE="/etc/vpp/vppcfg.yaml"
|
||||||
|
VPPCFG_VPP_FILE="/config/vpp/config/vppcfg.vpp"
|
||||||
|
CLAB_VPP_FILE="/config/vpp/config/clab.vpp"
|
||||||
|
BIRD_CONFIG="/etc/bird/bird.conf"
|
||||||
|
FFR_CONFIG="/etc/frr/frr.conf"
|
||||||
|
mkdir -p /config/vpp/config/
|
||||||
|
echo "comment { please upgrade to containerlab 0.75+ }" > /config/vpp/config/manual-pre.vpp
|
||||||
|
echo "comment { please upgrade to containerlab 0.75+ }" > /config/vpp/config/manual-post.vpp
|
||||||
|
else
|
||||||
|
echo "Initializing /config"
|
||||||
|
for dir in vpp bird frr; do
|
||||||
|
rsync -av --ignore-existing /etc/$dir/ /config/$dir/
|
||||||
|
rm -rf /etc/$dir/
|
||||||
|
ln -s /config/$dir /etc/$dir
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$BIRD_ENABLED" == "true" ]; then
|
if [ "$BIRD_ENABLED" == "true" ]; then
|
||||||
echo "Starting Bird in $NETNS"
|
echo "Starting Bird in $NETNS"
|
||||||
mkdir -p /run/bird /var/log/bird
|
mkdir -p /run/bird /var/log/bird
|
||||||
chown bird:bird /var/log/bird
|
chown bird:bird /var/log/bird
|
||||||
ROUTERID=$(ip -br a show eth0 | awk '{ print $3 }' | cut -f1 -d/)
|
ROUTERID=$(ip -br a show eth0 | awk '{ print $3 }' | cut -f1 -d/)
|
||||||
sed -i -e "s,.*router id .*,router id $ROUTERID; # Set by container-init.sh," /etc/bird/bird.conf
|
sed -i -e "s,.*router id .*,router id $ROUTERID; # Set by container-init.sh," $BIRD_CONFIG
|
||||||
/usr/bin/nsenter --net=/var/run/netns/$NETNS /usr/sbin/bird -u bird -g bird
|
/usr/bin/nsenter --net=/run/netns/$NETNS /usr/sbin/bird -u bird -g vpp -c $BIRD_CONFIG
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$FRR_ENABLED" == "true" ]; then
|
||||||
|
echo "Starting FRRouting in $NETNS"
|
||||||
|
ROUTERID=$(ip -br a show eth0 | awk '{ print $3 }' | cut -f1 -d/)
|
||||||
|
sed -i -e "s,^ip router-id .*,ip router-id $ROUTERID," $FRR_CONFIG
|
||||||
|
/etc/init.d/frr start
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Generating $CLAB_VPP_FILE"
|
echo "Generating $CLAB_VPP_FILE"
|
||||||
@@ -41,10 +74,15 @@ set interface state $IFNAME up
|
|||||||
EOF
|
EOF
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Generating $VPPCFG_VPP_FILE"
|
if [ -s $VPPCFG_YAML_FILE ]; then
|
||||||
: > $VPPCFG_VPP_FILE
|
echo "Generating $VPPCFG_YAML_FILE into $VPPCFG_VPP_FILE"
|
||||||
if [ -r /etc/vpp/vppcfg.yaml ]; then
|
: > $VPPCFG_VPP_FILE
|
||||||
vppcfg plan --novpp -c /etc/vpp/vppcfg.yaml -o $VPPCFG_VPP_FILE
|
if [ -r $VPPCFG_YAML_FILE ]; then
|
||||||
|
vppcfg plan --novpp -c $VPPCFG_YAML_FILE -o $VPPCFG_VPP_FILE
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Generating empty $VPPCFG_VPP_FILE due to missing or empty $VPPCFG_YAML_FILE"
|
||||||
|
echo "comment { please provide a vppcfg.yaml file }" > $VPPCFG_VPP_FILE
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Starting VPP"
|
echo "Starting VPP"
|
||||||
|
|||||||
BIN
learn-vpp.png
Normal file
BIN
learn-vpp.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
118
scripts/check-vppdebs.py
Executable file
118
scripts/check-vppdebs.py
Executable file
@@ -0,0 +1,118 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Validate a directory of locally-built VPP .deb packages before a Docker build.
|
||||||
|
|
||||||
|
Checks:
|
||||||
|
- The directory exists
|
||||||
|
- Exactly one *.changes file
|
||||||
|
- Exactly one *.buildinfo file
|
||||||
|
- Exactly one of each required .deb package
|
||||||
|
- All packages carry the same version string
|
||||||
|
- Optionally: version matches an expected value (for cross-machine consistency)
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
check-vppdebs.py [--print-version] [--assert-version VERSION] [directory]
|
||||||
|
|
||||||
|
--print-version Print only the detected version string and exit (no other output).
|
||||||
|
--assert-version VER After all checks pass, assert the detected version equals VER.
|
||||||
|
Use this to verify summer and jessica-orb have the same build.
|
||||||
|
directory Path to check (default: ~/src/vpp/build-root).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import glob
|
||||||
|
import argparse
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
REQUIRED_DEBS = [
|
||||||
|
"libvppinfra_*.deb",
|
||||||
|
"python3-vpp-api_*.deb",
|
||||||
|
"vpp_*.deb",
|
||||||
|
"vpp-crypto-engines_*.deb",
|
||||||
|
"vpp-plugin-core_*.deb",
|
||||||
|
]
|
||||||
|
|
||||||
|
def find(directory, pattern):
|
||||||
|
return sorted(glob.glob(str(directory / pattern)))
|
||||||
|
|
||||||
|
def version_from_deb(path):
|
||||||
|
"""Extract the version field from a deb filename: name_VERSION_arch.deb"""
|
||||||
|
return Path(path).stem.split("_")[1]
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(add_help=False)
|
||||||
|
parser.add_argument("--print-version", action="store_true")
|
||||||
|
parser.add_argument("--assert-version", metavar="VERSION", default=None)
|
||||||
|
parser.add_argument("directory", nargs="?", default="~/src/vpp/build-root")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
directory = Path(args.directory).expanduser()
|
||||||
|
errors = []
|
||||||
|
versions = []
|
||||||
|
|
||||||
|
if not args.print_version:
|
||||||
|
print(f"Checking VPP debs in: {directory}")
|
||||||
|
|
||||||
|
if not directory.is_dir():
|
||||||
|
print(f" ERROR: directory does not exist: {directory}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# *.changes
|
||||||
|
changes = find(directory, "*.changes")
|
||||||
|
if len(changes) == 1:
|
||||||
|
if not args.print_version:
|
||||||
|
print(f" OK changes : {Path(changes[0]).name}")
|
||||||
|
else:
|
||||||
|
errors.append(f"expected exactly 1 *.changes, found {len(changes)}: {[Path(f).name for f in changes]}")
|
||||||
|
|
||||||
|
# *.buildinfo
|
||||||
|
buildinfo = find(directory, "*.buildinfo")
|
||||||
|
if len(buildinfo) == 1:
|
||||||
|
if not args.print_version:
|
||||||
|
print(f" OK buildinfo : {Path(buildinfo[0]).name}")
|
||||||
|
else:
|
||||||
|
errors.append(f"expected exactly 1 *.buildinfo, found {len(buildinfo)}: {[Path(f).name for f in buildinfo]}")
|
||||||
|
|
||||||
|
# required debs
|
||||||
|
for pattern in REQUIRED_DEBS:
|
||||||
|
matches = find(directory, pattern)
|
||||||
|
if len(matches) == 1:
|
||||||
|
ver = version_from_deb(matches[0])
|
||||||
|
versions.append(ver)
|
||||||
|
if not args.print_version:
|
||||||
|
print(f" OK {pattern:<30s}: {Path(matches[0]).name}")
|
||||||
|
else:
|
||||||
|
errors.append(f"expected exactly 1 {pattern}, found {len(matches)}: {[Path(f).name for f in matches]}")
|
||||||
|
|
||||||
|
# version consistency within this directory
|
||||||
|
if versions and len(set(versions)) > 1:
|
||||||
|
errors.append(f"debs carry mixed versions: {sorted(set(versions))}")
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
if not args.print_version:
|
||||||
|
print()
|
||||||
|
for e in errors:
|
||||||
|
print(f" ERROR: {e}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
detected = versions[0] if versions else None
|
||||||
|
|
||||||
|
if args.print_version:
|
||||||
|
print(detected or "")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
print(f" OK version : {detected}")
|
||||||
|
|
||||||
|
# cross-machine version assertion
|
||||||
|
if args.assert_version:
|
||||||
|
if detected == args.assert_version:
|
||||||
|
print(f" OK matches summer : {detected}")
|
||||||
|
else:
|
||||||
|
print(f" ERROR: version mismatch: this machine={detected}, summer={args.assert_version}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("Preflight OK.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
57
tests/01-vpp-bird/01-e2e-lab.robot
Normal file
57
tests/01-vpp-bird/01-e2e-lab.robot
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
*** Settings ***
|
||||||
|
Library OperatingSystem
|
||||||
|
Resource ../ssh.robot
|
||||||
|
Resource ../common.robot
|
||||||
|
|
||||||
|
Suite Teardown Run Keyword Cleanup
|
||||||
|
|
||||||
|
|
||||||
|
*** Variables ***
|
||||||
|
${lab-name} e2e-vpp
|
||||||
|
${lab-file-name} e2e-lab/vpp.clab.yml
|
||||||
|
${runtime} docker
|
||||||
|
|
||||||
|
|
||||||
|
*** Test Cases ***
|
||||||
|
Deploy ${lab-name} lab
|
||||||
|
Log ${CURDIR}
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} deploy -t ${CURDIR}/${lab-file-name}
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
|
||||||
|
Pause to let OSPF converge
|
||||||
|
Sleep 20s
|
||||||
|
|
||||||
|
Check BFD Adjacencies
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=vpp2 --cmd "birdc show bfd ses"
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
Should Match Regexp ${output} (?m)fe80::.*eth2.*Up
|
||||||
|
Should Match Regexp ${output} (?m)10\.82\.98\..*eth2.*Up
|
||||||
|
|
||||||
|
Check OSPF IPv4 Adjacency
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=vpp1 --cmd "birdc show ospf nei ospf4"
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
Should Match Regexp ${output} (?m)Full/PtP.*eth2
|
||||||
|
|
||||||
|
Check OSPF IPv6 Adjacency
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=vpp2 --cmd "birdc show ospf nei ospf6"
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
Should Match Regexp ${output} (?m)Full/PtP.*eth2
|
||||||
|
|
||||||
|
Ensure client1 can ping client2
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=client1 --cmd "ping -c 5 10.82.98.82"
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
Should Contain ${output} 5 packets transmitted, 4 packets received, 20% packet loss
|
||||||
|
|
||||||
|
*** Keywords ***
|
||||||
|
Cleanup
|
||||||
|
Run ${CLAB_BIN} --runtime ${runtime} destroy -t ${CURDIR}/${lab-file-name} --cleanup
|
||||||
19
tests/01-vpp-bird/e2e-lab/config/vpp1/bird-local.conf
Normal file
19
tests/01-vpp-bird/e2e-lab/config/vpp1/bird-local.conf
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
protocol bfd bfd1 {
|
||||||
|
interface "eth2" { interval 100 ms; multiplier 30; };
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol ospf v2 ospf4 {
|
||||||
|
ipv4 { import all; export all; };
|
||||||
|
area 0 {
|
||||||
|
interface "loop0" { stub yes; };
|
||||||
|
interface "eth2" { type pointopoint; cost 10; bfd on; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol ospf v3 ospf6 {
|
||||||
|
ipv6 { import all; export all; };
|
||||||
|
area 0 {
|
||||||
|
interface "loop0" { stub yes; };
|
||||||
|
interface "eth2" { type pointopoint; cost 10; bfd on; };
|
||||||
|
};
|
||||||
|
}
|
||||||
16
tests/01-vpp-bird/e2e-lab/config/vpp1/vppcfg.yaml
Normal file
16
tests/01-vpp-bird/e2e-lab/config/vpp1/vppcfg.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
interfaces:
|
||||||
|
eth1:
|
||||||
|
description: "To client1"
|
||||||
|
mtu: 1500
|
||||||
|
lcp: eth1
|
||||||
|
addresses: [10.82.98.65/28, 2001:db8:8298:101::1/64]
|
||||||
|
eth2:
|
||||||
|
description: "To vpp2"
|
||||||
|
mtu: 9216
|
||||||
|
lcp: eth2
|
||||||
|
addresses: [10.82.98.16/31, 2001:db8:8298:1::1/64]
|
||||||
|
loopbacks:
|
||||||
|
loop0:
|
||||||
|
description: "vpp1"
|
||||||
|
lcp: loop0
|
||||||
|
addresses: [10.82.98.0/32, 2001:db8:8298::/128]
|
||||||
19
tests/01-vpp-bird/e2e-lab/config/vpp2/bird-local.conf
Normal file
19
tests/01-vpp-bird/e2e-lab/config/vpp2/bird-local.conf
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
protocol bfd bfd1 {
|
||||||
|
interface "eth2" { interval 100 ms; multiplier 30; };
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol ospf v2 ospf4 {
|
||||||
|
ipv4 { import all; export all; };
|
||||||
|
area 0 {
|
||||||
|
interface "loop0" { stub yes; };
|
||||||
|
interface "eth2" { type pointopoint; cost 10; bfd on; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protocol ospf v3 ospf6 {
|
||||||
|
ipv6 { import all; export all; };
|
||||||
|
area 0 {
|
||||||
|
interface "loop0" { stub yes; };
|
||||||
|
interface "eth2" { type pointopoint; cost 10; bfd on; };
|
||||||
|
};
|
||||||
|
}
|
||||||
16
tests/01-vpp-bird/e2e-lab/config/vpp2/vppcfg.yaml
Normal file
16
tests/01-vpp-bird/e2e-lab/config/vpp2/vppcfg.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
interfaces:
|
||||||
|
eth1:
|
||||||
|
description: "To client2"
|
||||||
|
mtu: 1500
|
||||||
|
lcp: eth1
|
||||||
|
addresses: [10.82.98.81/28, 2001:db8:8298:102::1/64]
|
||||||
|
eth2:
|
||||||
|
description: "To vpp1"
|
||||||
|
mtu: 9216
|
||||||
|
lcp: eth2
|
||||||
|
addresses: [10.82.98.17/31, 2001:db8:8298:1::2/64]
|
||||||
|
loopbacks:
|
||||||
|
loop0:
|
||||||
|
description: "vpp2"
|
||||||
|
lcp: loop0
|
||||||
|
addresses: [10.82.98.1/32, 2001:db8:8298::1/128]
|
||||||
38
tests/01-vpp-bird/e2e-lab/vpp.clab.yml
Normal file
38
tests/01-vpp-bird/e2e-lab/vpp.clab.yml
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
name: e2e-vpp
|
||||||
|
|
||||||
|
topology:
|
||||||
|
kinds:
|
||||||
|
fdio_vpp:
|
||||||
|
image: ${IMAGE}
|
||||||
|
startup-config: config/__clabNodeName__/vppcfg.yaml
|
||||||
|
binds:
|
||||||
|
- config/__clabNodeName__/bird-local.conf:/config/bird/bird-local.conf:ro
|
||||||
|
linux:
|
||||||
|
image: alpine:latest
|
||||||
|
|
||||||
|
nodes:
|
||||||
|
vpp1:
|
||||||
|
kind: fdio_vpp
|
||||||
|
vpp2:
|
||||||
|
kind: fdio_vpp
|
||||||
|
client1:
|
||||||
|
kind: linux
|
||||||
|
exec:
|
||||||
|
- ip link set address 00:c1:ab:00:00:01 dev eth1
|
||||||
|
- ip addr add 10.82.98.66/28 dev eth1
|
||||||
|
- ip route add 10.82.98.0/24 via 10.82.98.65
|
||||||
|
- ip addr add 2001:db8:8298:101::2/64 dev eth1
|
||||||
|
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:101::1
|
||||||
|
client2:
|
||||||
|
kind: linux
|
||||||
|
exec:
|
||||||
|
- ip link set address 00:c1:ab:00:00:02 dev eth1
|
||||||
|
- ip addr add 10.82.98.82/28 dev eth1
|
||||||
|
- ip route add 10.82.98.0/24 via 10.82.98.81
|
||||||
|
- ip addr add 2001:db8:8298:102::2/64 dev eth1
|
||||||
|
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:102::1
|
||||||
|
|
||||||
|
links:
|
||||||
|
- endpoints: ["vpp1:eth2", "vpp2:eth2"]
|
||||||
|
- endpoints: ["client1:eth1", "vpp1:eth1"]
|
||||||
|
- endpoints: ["client2:eth1", "vpp2:eth1"]
|
||||||
58
tests/02-vpp-frr/01-e2e-lab.robot
Normal file
58
tests/02-vpp-frr/01-e2e-lab.robot
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
*** Settings ***
|
||||||
|
Library OperatingSystem
|
||||||
|
Resource ../ssh.robot
|
||||||
|
Resource ../common.robot
|
||||||
|
|
||||||
|
Suite Teardown Run Keyword Cleanup
|
||||||
|
|
||||||
|
|
||||||
|
*** Variables ***
|
||||||
|
${lab-name} e2e-vpp
|
||||||
|
${lab-file-name} e2e-lab/vpp.clab.yml
|
||||||
|
${runtime} docker
|
||||||
|
|
||||||
|
|
||||||
|
*** Test Cases ***
|
||||||
|
Deploy ${lab-name} lab
|
||||||
|
Log ${CURDIR}
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} deploy -t ${CURDIR}/${lab-file-name}
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
|
||||||
|
Pause to let OSPF converge
|
||||||
|
Sleep 20s
|
||||||
|
|
||||||
|
Check BFD Adjacencies
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=vpp1 --cmd "vtysh -c 'show bfd peers brief'"
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
Should Match Regexp ${output} (?m)10\.82\.98\..*10\.82\.98\..*up
|
||||||
|
Should Match Regexp ${output} (?m)fe80::.*fe80::.*up
|
||||||
|
|
||||||
|
Check OSPF IPv4 Adjacency
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=vpp1 --cmd "vtysh -c 'show ip ospf nei'"
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
Should Match Regexp ${output} (?m)Full/.*eth2
|
||||||
|
|
||||||
|
Check OSPF IPv6 Adjacency
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=vpp2 --cmd "vtysh -c 'show ipv6 ospf nei'"
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
Should Match Regexp ${output} (?m)Full/.*eth2
|
||||||
|
|
||||||
|
Ensure client1 can ping client2
|
||||||
|
${rc} ${output} = Run And Return Rc And Output
|
||||||
|
... ${CLAB_BIN} --runtime ${runtime} exec -t ${CURDIR}/${lab-file-name} --label clab-node-name\=client1 --cmd "ping -c 5 10.82.98.82"
|
||||||
|
Log ${output}
|
||||||
|
Should Be Equal As Integers ${rc} 0
|
||||||
|
Should Contain ${output} 5 packets transmitted, 4 packets received, 20% packet loss
|
||||||
|
|
||||||
|
|
||||||
|
*** Keywords ***
|
||||||
|
Cleanup
|
||||||
|
Run ${CLAB_BIN} --runtime ${runtime} destroy -t ${CURDIR}/${lab-file-name} --cleanup
|
||||||
31
tests/02-vpp-frr/e2e-lab/config/vpp1/frr.conf
Normal file
31
tests/02-vpp-frr/e2e-lab/config/vpp1/frr.conf
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
frr version 10.3
|
||||||
|
frr defaults traditional
|
||||||
|
hostname vpp1
|
||||||
|
log syslog informational
|
||||||
|
service integrated-vtysh-config
|
||||||
|
!
|
||||||
|
ip router-id 10.82.98.0
|
||||||
|
!
|
||||||
|
interface eth2
|
||||||
|
ip ospf area 0
|
||||||
|
ip ospf bfd
|
||||||
|
ip ospf cost 10
|
||||||
|
ip ospf network point-to-point
|
||||||
|
ipv6 ospf6 area 0
|
||||||
|
ipv6 ospf6 bfd
|
||||||
|
ipv6 ospf6 cost 10
|
||||||
|
ipv6 ospf6 network point-to-point
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
interface loop0
|
||||||
|
ip ospf passive
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
router ospf
|
||||||
|
redistribute connected
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
router ospf6
|
||||||
|
redistribute connected
|
||||||
|
exit
|
||||||
|
!
|
||||||
16
tests/02-vpp-frr/e2e-lab/config/vpp1/vppcfg.yaml
Normal file
16
tests/02-vpp-frr/e2e-lab/config/vpp1/vppcfg.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
interfaces:
|
||||||
|
eth1:
|
||||||
|
description: "To client1"
|
||||||
|
mtu: 1500
|
||||||
|
lcp: eth1
|
||||||
|
addresses: [10.82.98.65/28, 2001:db8:8298:101::1/64]
|
||||||
|
eth2:
|
||||||
|
description: "To vpp2"
|
||||||
|
mtu: 9216
|
||||||
|
lcp: eth2
|
||||||
|
addresses: [10.82.98.16/31, 2001:db8:8298:1::1/64]
|
||||||
|
loopbacks:
|
||||||
|
loop0:
|
||||||
|
description: "vpp1"
|
||||||
|
lcp: loop0
|
||||||
|
addresses: [10.82.98.0/32, 2001:db8:8298::/128]
|
||||||
31
tests/02-vpp-frr/e2e-lab/config/vpp2/frr.conf
Normal file
31
tests/02-vpp-frr/e2e-lab/config/vpp2/frr.conf
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
frr version 10.3
|
||||||
|
frr defaults traditional
|
||||||
|
hostname vpp2
|
||||||
|
log syslog informational
|
||||||
|
service integrated-vtysh-config
|
||||||
|
!
|
||||||
|
ip router-id 10.82.98.1
|
||||||
|
!
|
||||||
|
interface eth2
|
||||||
|
ip ospf area 0
|
||||||
|
ip ospf bfd
|
||||||
|
ip ospf cost 10
|
||||||
|
ip ospf network point-to-point
|
||||||
|
ipv6 ospf6 area 0
|
||||||
|
ipv6 ospf6 bfd
|
||||||
|
ipv6 ospf6 cost 10
|
||||||
|
ipv6 ospf6 network point-to-point
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
interface loop0
|
||||||
|
ip ospf passive
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
router ospf
|
||||||
|
redistribute connected
|
||||||
|
exit
|
||||||
|
!
|
||||||
|
router ospf6
|
||||||
|
redistribute connected
|
||||||
|
exit
|
||||||
|
!
|
||||||
16
tests/02-vpp-frr/e2e-lab/config/vpp2/vppcfg.yaml
Normal file
16
tests/02-vpp-frr/e2e-lab/config/vpp2/vppcfg.yaml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
interfaces:
|
||||||
|
eth1:
|
||||||
|
description: "To client2"
|
||||||
|
mtu: 1500
|
||||||
|
lcp: eth1
|
||||||
|
addresses: [10.82.98.81/28, 2001:db8:8298:102::1/64]
|
||||||
|
eth2:
|
||||||
|
description: "To vpp1"
|
||||||
|
mtu: 9216
|
||||||
|
lcp: eth2
|
||||||
|
addresses: [10.82.98.17/31, 2001:db8:8298:1::2/64]
|
||||||
|
loopbacks:
|
||||||
|
loop0:
|
||||||
|
description: "vpp2"
|
||||||
|
lcp: loop0
|
||||||
|
addresses: [10.82.98.1/32, 2001:db8:8298::1/128]
|
||||||
41
tests/02-vpp-frr/e2e-lab/vpp.clab.yml
Normal file
41
tests/02-vpp-frr/e2e-lab/vpp.clab.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: e2e-vpp
|
||||||
|
|
||||||
|
topology:
|
||||||
|
kinds:
|
||||||
|
fdio_vpp:
|
||||||
|
image: ${IMAGE}
|
||||||
|
startup-config: config/__clabNodeName__/vppcfg.yaml
|
||||||
|
binds:
|
||||||
|
- config/__clabNodeName__/frr.conf:/config/frr/frr.conf:ro
|
||||||
|
env:
|
||||||
|
BIRD_ENABLED: false
|
||||||
|
FRR_ENABLED: true
|
||||||
|
linux:
|
||||||
|
image: alpine:latest
|
||||||
|
|
||||||
|
nodes:
|
||||||
|
vpp1:
|
||||||
|
kind: fdio_vpp
|
||||||
|
vpp2:
|
||||||
|
kind: fdio_vpp
|
||||||
|
client1:
|
||||||
|
kind: linux
|
||||||
|
exec:
|
||||||
|
- ip link set address 00:c1:ab:00:00:01 dev eth1
|
||||||
|
- ip addr add 10.82.98.66/28 dev eth1
|
||||||
|
- ip route add 10.82.98.0/24 via 10.82.98.65
|
||||||
|
- ip addr add 2001:db8:8298:101::2/64 dev eth1
|
||||||
|
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:101::1
|
||||||
|
client2:
|
||||||
|
kind: linux
|
||||||
|
exec:
|
||||||
|
- ip link set address 00:c1:ab:00:00:02 dev eth1
|
||||||
|
- ip addr add 10.82.98.82/28 dev eth1
|
||||||
|
- ip route add 10.82.98.0/24 via 10.82.98.81
|
||||||
|
- ip addr add 2001:db8:8298:102::2/64 dev eth1
|
||||||
|
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:102::1
|
||||||
|
|
||||||
|
links:
|
||||||
|
- endpoints: ["vpp1:eth2", "vpp2:eth2"]
|
||||||
|
- endpoints: ["client1:eth1", "vpp1:eth1"]
|
||||||
|
- endpoints: ["client2:eth1", "vpp2:eth1"]
|
||||||
2
tests/common.robot
Normal file
2
tests/common.robot
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
*** Variables ***
|
||||||
|
${CLAB_BIN} containerlab
|
||||||
2
tests/requirements.txt
Normal file
2
tests/requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
robotframework
|
||||||
|
robotframework-sshlibrary
|
||||||
45
tests/rf-run.sh
Executable file
45
tests/rf-run.sh
Executable file
@@ -0,0 +1,45 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Run Robot Framework tests for vpp-containerlab.
|
||||||
|
# Arguments:
|
||||||
|
# $1 - container runtime: [docker, podman]
|
||||||
|
# $2 - test suite path (directory or .robot file)
|
||||||
|
#
|
||||||
|
# Environment variables:
|
||||||
|
# CLAB_BIN - path to containerlab binary (default: containerlab)
|
||||||
|
# IMAGE - docker image to use in topology (must be set)
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
if [ -z "${CLAB_BIN}" ]; then
|
||||||
|
CLAB_BIN=containerlab
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "${IMAGE}" ]; then
|
||||||
|
echo "ERROR: IMAGE environment variable must be set" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
mkdir -p "${SCRIPT_DIR}/out"
|
||||||
|
|
||||||
|
source "${SCRIPT_DIR}/.venv/bin/activate"
|
||||||
|
|
||||||
|
function get_logname() {
|
||||||
|
path=$1
|
||||||
|
filename=$(basename "$path")
|
||||||
|
if [[ "$filename" == *.* ]]; then
|
||||||
|
dirname=$(dirname "$path")
|
||||||
|
basename=$(basename "$path" | cut -d. -f1)
|
||||||
|
echo "${dirname##*/}-${basename}"
|
||||||
|
else
|
||||||
|
echo "${filename}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
robot --consolecolors on -r none \
|
||||||
|
--variable CLAB_BIN:"${CLAB_BIN}" \
|
||||||
|
--variable runtime:"$1" \
|
||||||
|
-l "${SCRIPT_DIR}/out/$(get_logname $2)-$1-log" \
|
||||||
|
--output "${SCRIPT_DIR}/out/$(get_logname $2)-$1-out.xml" \
|
||||||
|
"$2"
|
||||||
44
tests/ssh.robot
Normal file
44
tests/ssh.robot
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
*** Settings ***
|
||||||
|
Library SSHLibrary
|
||||||
|
|
||||||
|
|
||||||
|
*** Keywords ***
|
||||||
|
Login via SSH with username and password
|
||||||
|
[Arguments]
|
||||||
|
... ${address}=${None}
|
||||||
|
... ${port}=22
|
||||||
|
... ${username}=${None}
|
||||||
|
... ${password}=${None}
|
||||||
|
# seconds to try and successfully login
|
||||||
|
... ${try_for}=4
|
||||||
|
... ${conn_timeout}=3
|
||||||
|
FOR ${i} IN RANGE ${try_for}
|
||||||
|
SSHLibrary.Open Connection ${address} timeout=${conn_timeout}
|
||||||
|
${status}= Run Keyword And Return Status SSHLibrary.Login ${username} ${password}
|
||||||
|
IF ${status} BREAK
|
||||||
|
Sleep 1s
|
||||||
|
END
|
||||||
|
IF $status!=True
|
||||||
|
Fail Unable to connect to ${address} via SSH in ${try_for} attempts
|
||||||
|
END
|
||||||
|
Log Exited the loop.
|
||||||
|
|
||||||
|
Login via SSH with public key
|
||||||
|
[Arguments]
|
||||||
|
... ${address}=${None}
|
||||||
|
... ${port}=22
|
||||||
|
... ${username}=${None}
|
||||||
|
... ${keyfile}=${None}
|
||||||
|
... ${try_for}=4
|
||||||
|
... ${conn_timeout}=3
|
||||||
|
FOR ${i} IN RANGE ${try_for}
|
||||||
|
SSHLibrary.Open Connection ${address} timeout=${conn_timeout}
|
||||||
|
${status}= Run Keyword And Return Status SSHLibrary.Login With Public Key
|
||||||
|
... ${username} ${keyfile}
|
||||||
|
IF ${status} BREAK
|
||||||
|
Sleep 1s
|
||||||
|
END
|
||||||
|
IF $status!=True
|
||||||
|
Fail Unable to connect to ${address} via SSH in ${try_for} attempts
|
||||||
|
END
|
||||||
|
Log Exited the loop.
|
||||||
39
vpp-bird.clab.yml
Normal file
39
vpp-bird.clab.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: learn-vpp
|
||||||
|
prefix: ""
|
||||||
|
|
||||||
|
topology:
|
||||||
|
kinds:
|
||||||
|
fdio_vpp:
|
||||||
|
image: git.ipng.ch/ipng/vpp-containerlab:stable
|
||||||
|
startup-config: config/__clabNodeName__/vppcfg.yaml
|
||||||
|
binds:
|
||||||
|
- config/__clabNodeName__/bird-local.conf:/config/bird/bird-local.conf:ro
|
||||||
|
linux:
|
||||||
|
image: ghcr.io/srl-labs/network-multitool:latest
|
||||||
|
|
||||||
|
nodes:
|
||||||
|
vpp1:
|
||||||
|
kind: fdio_vpp
|
||||||
|
vpp2:
|
||||||
|
kind: fdio_vpp
|
||||||
|
client1:
|
||||||
|
kind: linux
|
||||||
|
exec:
|
||||||
|
- ip link set address 00:c1:ab:00:00:01 mtu 1500 dev eth1
|
||||||
|
- ip addr add 10.82.98.66/28 dev eth1
|
||||||
|
- ip route add 10.82.98.0/24 via 10.82.98.65
|
||||||
|
- ip addr add 2001:db8:8298:101::2/64 dev eth1
|
||||||
|
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:101::1
|
||||||
|
client2:
|
||||||
|
kind: linux
|
||||||
|
exec:
|
||||||
|
- ip link set address 00:c1:ab:00:00:02 mtu 1500 dev eth1
|
||||||
|
- ip addr add 10.82.98.82/28 dev eth1
|
||||||
|
- ip route add 10.82.98.0/24 via 10.82.98.81
|
||||||
|
- ip addr add 2001:db8:8298:102::2/64 dev eth1
|
||||||
|
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:102::1
|
||||||
|
|
||||||
|
links:
|
||||||
|
- endpoints: ["vpp1:eth2", "vpp2:eth2"]
|
||||||
|
- endpoints: ["client1:eth1", "vpp1:eth1"]
|
||||||
|
- endpoints: ["client2:eth1", "vpp2:eth1"]
|
||||||
41
vpp-frr.clab.yml
Normal file
41
vpp-frr.clab.yml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
name: learn-vpp
|
||||||
|
prefix: ""
|
||||||
|
|
||||||
|
topology:
|
||||||
|
kinds:
|
||||||
|
fdio_vpp:
|
||||||
|
image: git.ipng.ch/ipng/vpp-containerlab:stable
|
||||||
|
startup-config: config/__clabNodeName__/vppcfg.yaml
|
||||||
|
binds:
|
||||||
|
- config/__clabNodeName__/frr.conf:/config/frr/frr.conf:rw
|
||||||
|
env-files:
|
||||||
|
- config/lab-frr.env
|
||||||
|
linux:
|
||||||
|
image: alpine:latest
|
||||||
|
|
||||||
|
nodes:
|
||||||
|
vpp1:
|
||||||
|
kind: fdio_vpp
|
||||||
|
vpp2:
|
||||||
|
kind: fdio_vpp
|
||||||
|
client1:
|
||||||
|
kind: linux
|
||||||
|
exec:
|
||||||
|
- ip link set address 00:c1:ab:00:00:01 mtu 1500 dev eth1
|
||||||
|
- ip addr add 10.82.98.66/28 dev eth1
|
||||||
|
- ip route add 10.82.98.0/24 via 10.82.98.65
|
||||||
|
- ip addr add 2001:db8:8298:101::2/64 dev eth1
|
||||||
|
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:101::1
|
||||||
|
client2:
|
||||||
|
kind: linux
|
||||||
|
exec:
|
||||||
|
- ip link set address 00:c1:ab:00:00:02 mtu 1500 dev eth1
|
||||||
|
- ip addr add 10.82.98.82/28 dev eth1
|
||||||
|
- ip route add 10.82.98.0/24 via 10.82.98.81
|
||||||
|
- ip addr add 2001:db8:8298:102::2/64 dev eth1
|
||||||
|
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:102::1
|
||||||
|
|
||||||
|
links:
|
||||||
|
- endpoints: ["vpp1:eth2", "vpp2:eth2"]
|
||||||
|
- endpoints: ["client1:eth1", "vpp1:eth1"]
|
||||||
|
- endpoints: ["client2:eth1", "vpp2:eth1"]
|
||||||
42
vpp.clab.yml
42
vpp.clab.yml
@@ -1,42 +0,0 @@
|
|||||||
name: learn-vpp
|
|
||||||
prefix: ""
|
|
||||||
|
|
||||||
topology:
|
|
||||||
kinds:
|
|
||||||
fdio_vpp:
|
|
||||||
image: git.ipng.ch/ipng/vpp-containerlab:latest
|
|
||||||
linux:
|
|
||||||
image: alpine:latest
|
|
||||||
|
|
||||||
nodes:
|
|
||||||
vpp1:
|
|
||||||
kind: fdio_vpp
|
|
||||||
binds:
|
|
||||||
- config/vpp1/vppcfg.yaml:/etc/vpp/vppcfg.yaml:ro
|
|
||||||
- config/vpp1/bird-local.conf:/etc/bird/bird-local.conf:ro
|
|
||||||
vpp2:
|
|
||||||
kind: fdio_vpp
|
|
||||||
binds:
|
|
||||||
- config/vpp2/vppcfg.yaml:/etc/vpp/vppcfg.yaml:ro
|
|
||||||
- config/vpp2/bird-local.conf:/etc/bird/bird-local.conf:ro
|
|
||||||
client1:
|
|
||||||
kind: linux
|
|
||||||
exec:
|
|
||||||
- ip link set address 00:c1:ab:00:00:01 dev eth1
|
|
||||||
- ip addr add 10.82.98.66/28 dev eth1
|
|
||||||
- ip route add 10.82.98.0/24 via 10.82.98.65
|
|
||||||
- ip addr add 2001:db8:8298:101::2/64 dev eth1
|
|
||||||
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:101::1
|
|
||||||
client2:
|
|
||||||
kind: linux
|
|
||||||
exec:
|
|
||||||
- ip link set address 00:c1:ab:00:00:02 dev eth1
|
|
||||||
- ip addr add 10.82.98.82/28 dev eth1
|
|
||||||
- ip route add 10.82.98.0/24 via 10.82.98.81
|
|
||||||
- ip addr add 2001:db8:8298:102::2/64 dev eth1
|
|
||||||
- ip route add 2001:db8:8298::/48 via 2001:db8:8298:102::1
|
|
||||||
|
|
||||||
links:
|
|
||||||
- endpoints: ["vpp1:eth2", "vpp2:eth2"]
|
|
||||||
- endpoints: ["client1:eth1", "vpp1:eth1"]
|
|
||||||
- endpoints: ["client2:eth1", "vpp2:eth1"]
|
|
||||||
1
vpp.clab.yml
Symbolic link
1
vpp.clab.yml
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
vpp-bird.clab.yml
|
||||||
Reference in New Issue
Block a user