
Welcome to the final installment of our comprehensive Containerlab network automation series. In this part, we’ll build a simple lab featuring four routers running simple circular OSPF topology, and we complete it with automated data collection using Nornir. Quick Recap of Our ContainerLab Journey
Before diving into today’s advanced lab, let’s briefly review what we’ve accomplished in this series:
Part 1: Containerlab Foundations introduced us to the basics of ContainerLab installation, simple topology creation, and container networking fundamentals. We learned how ContainerLab revolutionizes network lab creation by using containerized network operating systems.
Part 2: NetOps Server and Routing expanded our capabilities by setting up a dedicated NetOps server for automation tasks and implementing advanced routing scenarios between containers. We established the foundation for enterprise-grade network automation workflows.
Today’s Part 3 takes us to the next level with a complex multi-router OSPF topology, complete with automated data collection using Nornir – essential skills for modern network automation engineers.
Let’s start by launching our lab infrastructure using Google Cloud CLI we prepared in our previous articles. This approach ensures we have sufficient resources for our ContainerLab automation lab.
# Start the NetOps automation server
gcloud compute instances start netops-server
# Start the Containerlab host server
gcloud compute instances start containerlab-server
# Verifying status of GCP instances
gcloud compute instances list
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
containerlab-server us-central1-a n1-standard-4 10.128.0.5 public_ip RUNNING
netops us-central1-a e2-medium 10.128.0.4 public_ip RUNNINGEnsure both servers can communicate, this should be possible by default:
# Test connectivity between servers
user@netops:~$ ping -c 3 10.128.0.5
PING 10.128.0.5 (10.128.0.5) 56(84) bytes of data.
64 bytes from 10.128.0.5: icmp_seq=1 ttl=64 time=2.02 ms
64 bytes from 10.128.0.5: icmp_seq=2 ttl=64 time=0.312 ms
64 bytes from 10.128.0.5: icmp_seq=3 ttl=64 time=0.291 ms
--- 10.128.0.5 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2019ms
rtt min/avg/max/mdev = 0.291/0.874/2.021/0.810 ms
Our ContainerLab OSPF topology will consist of four routers (R1-R4) connected in a circular pattern. This design simulates an enterprise network segment.
Create the ContainerLab YAML configuration that defines our entire topology:
name: ospf_lab
topology:
nodes:
router1:
kind: ceos
image: ceos:4.29.9.1M
startup-config: router1.cfg
mgmt-ipv4: 172.20.20.11
router2:
kind: ceos
image: ceos:4.29.9.1M
startup-config: router2.cfg
mgmt-ipv4: 172.20.20.12
router3:
kind: ceos
image: ceos:4.29.9.1M
startup-config: router3.cfg
mgmt-ipv4: 172.20.20.13
router4:
kind: ceos
image: ceos:4.29.9.1M
startup-config: router4.cfg
mgmt-ipv4: 172.20.20.14
links:
# Create a ring topology: R1 -- R2 -- R3 -- R4 -- R1
- endpoints: ["router1:eth1", "router2:eth1"]
- endpoints: ["router2:eth2", "router3:eth1"]
- endpoints: ["router3:eth2", "router4:eth1"]
- endpoints: ["router4:eth2", "router1:eth2"]We are using traditional approach – building the basic topology with ContainerLab and adding more config for each router with config file. The config file contains:
Now let’s deploy our ContainerLab network automation environment:
# Deploy the lab topology
containerlab deploy -t ospf-lab.yml
# Verify all containers are running
sudo containerlab inspect -t ospf-lab.yml
╭──────────────────────────┬────────────────┬─────────┬───────────────────╮
│ Name │ Kind/Image │ State │ IPv4/6 Address │
├──────────────────────────┼────────────────┼─────────┼───────────────────┤
│ clab-arista_ospf-router1 │ ceos │ running │ 172.20.20.11 │
│ │ ceos:4.29.9.1M │ │ 3fff:172:20:20::6 │
├──────────────────────────┼────────────────┼─────────┼───────────────────┤
│ clab-arista_ospf-router2 │ ceos │ running │ 172.20.20.12 │
│ │ ceos:4.29.9.1M │ │ 3fff:172:20:20::8 │
├──────────────────────────┼────────────────┼─────────┼───────────────────┤
│ clab-arista_ospf-router3 │ ceos │ running │ 172.20.20.13 │
│ │ ceos:4.29.9.1M │ │ 3fff:172:20:20::9 │
├──────────────────────────┼────────────────┼─────────┼───────────────────┤
│ clab-arista_ospf-router4 │ ceos │ running │ 172.20.20.14 │
│ │ ceos:4.29.9.1M │ │ 3fff:172:20:20::7 │
╰──────────────────────────┴────────────────┴─────────┴───────────────────╯
# Check container status
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES STATUS PORTS
clab-arista_ospf-router2 Up 6 minutes
clab-arista_ospf-router3 Up 6 minutes
clab-arista_ospf-router4 Up 6 minutes
clab-arista_ospf-router1 Up 6 minutes
Before starting the collection straight away, I’d recommend testing connectivity first, you may need to update IPTABLES on the containerlab-server to allow the traffic to 172.20.20.0/24 subnet.
ssh admin@172.20.20.11
(admin@172.20.20.11) Password:
Last login: Wed Jun 11 19:18:21 2025 from 10.128.0.4
router1>Now comes the exciting part – automated network data collection using Nornir from our NetOps server.
Connect to your NetOps server and prepare the automation environment. Keep in mind, that we should already have all our packages on the VM in our netops venv:
# Load netops venv
source /opt/netops/venv/bin/activate
# Clone the Nornir facts repository
git clone https://gitlab.com/netodata/simple-facts.git
cd simple-facts
# Install required dependencies (just in case)
pip3 install -r requirements.txt
Create the Nornir inventory file for our lab devices:
#inventory/hosts.yaml
---
router1:
hostname: 172.20.20.11
groups:
- arista_eos
data:
container_name: clab-arista_ospf-router1
router2:
hostname: 172.20.20.12
groups:
- arista_eos
data:
container_name: clab-arista_ospf-router2
router3:
hostname: 172.20.20.13
groups:
- arista_eos
data:
container_name: clab-arista_ospf-router3
router4:
hostname: 172.20.20.14
groups:
- arista_eos
data:
container_name: clab-arista_ospf-router4#inventory/groups.yaml
---
arista_eos:
connection_options:
netmiko:
platform: arista_eos
extras:
global_delay_factor: 2
napalm:
platform: eos
data:
vendor: arista
nos: eos
device_type: router#inventory/defaults.yaml
---
username: admin
password: admin
platform: eos
port: 22
data:
site: ospf-labExecute the Nornir network automation script to collect comprehensive lab data:
# Run the facts collection script inside venv
python3 nornir_facts.py
*** OUTPUT OMMITED ***
^^^^ END Collecting Facts ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
✅ Facts collected and saved to /home/milan.zapletal/simple-facts/output/nornir_facts_20250611_193324.json
# Review collected data
ls output/
nornir_facts_20250611_181229.json
# Exploring collection
cat nornir_facts_*.json | jq 'keys[]'
"router1"
"router2"
"router3"
"router4"
cat nornir_facts_*.json | jq '.router1 | keys[]'
"show interfaces"
"show ip interface"
"show lldp neighbors"
"show running-config"
"show version"
# Getting uptime
cat nornir_facts_*.json | jq -r 'to_entries[] | "\(.key): \(.value."show version" | split("\n") | map(select(contains("Uptime"))) | .[0])"'
router1: Uptime: 1 hour and 53 minutes
router2: Uptime: 1 hour and 53 minutes
router3: Uptime: 1 hour and 53 minutes
router4: Uptime: 1 hour and 53 minutes
# Getting version
cat nornir_facts_*.json | jq -r 'to_entries[] | "\(.key): \(.value."show version" | split("\n") | map(select(contains("Software image version"))) | .[0] | split(": ")[1])"'
router1: 4.29.9.1M-38626060.42991M (engineering build)
router2: 4.29.9.1M-38626060.42991M (engineering build)
router3: 4.29.9.1M-38626060.42991M (engineering build)
router4: 4.29.9.1M-38626060.42991M (engineering build)
The script will automatically collect:
The Nornir automation script provides valuable insights into our lab environment:
When implementing containerlab automation labs in production:
# Check container logs for issues
sudo docker logs clab-ospf-automation-lab-R1
# Verify network connectivity
sudo containerlab exec -t ospf-lab.yml R1 "show ip interface brief"
# Restart problematic containers
sudo docker restart clab-ospf-automation-lab-R1
# Clear OSPF processes if needed
sudo containerlab exec -t ospf-lab.yml R1 "clear ip ospf process"
# Check OSPF database consistency
sudo containerlab exec -t ospf-lab.yml R1 "show ip ospf database"
This comprehensive containerlab OSPF lab demonstrates the power of combining containerized network operating systems with modern automation tools. We’ve successfully:
The combination of ContainerLab network automation with modern DevOps practices opens unlimited possibilities for network engineers looking to enhance their automation skills.
Related posts:
Ready to implement your own ContainerLab automation environment? Start with our basic labs and gradually build complexity. The future of network engineering lies in automation, and ContainerLab provides the perfect platform for mastering these essential skills.