Intended for Network Systems Programming Group Project.
This project provisions three Cisco IOS XE CSR1000v routers and deploys a Docker Compose monitoring stack with SNMP Exporter, Prometheus, and Grafana.
zodiac/
├── README.md
├── Makefile
├── cmd/
│ └── alert-receiver/
│ └── main.go
├── go.mod
├── go.sum
├── ansible.cfg
├── backups/
├── group_vars/
│ └── ios_routers.yml
├── host_vars/
│ ├── rtr-binus-alsut.yml
│ ├── rtr-binus-anggrek.yml
│ └── rtr-binus-inter.yml
├── inventory/
│ └── hosts.yml
├── monitoring/
│ ├── docker-compose.yml
│ ├── grafana/
│ │ ├── dashboards/
│ │ │ └── cisco-ios-snmp.json
│ │ └── provisioning/
│ │ ├── dashboards/
│ │ │ └── cisco-ios.yml
│ │ └── datasources/
│ │ └── prometheus.yml
│ ├── prometheus.yml
│ └── snmp.yml
├── playbooks/
│ ├── 01_check_reachability.yml
│ ├── 02_backup_config.yml
│ ├── 03_baseline_config.yml
│ ├── 04_routing_config.yml
│ ├── 05_verify.yml
│ └── 06_save_config.yml
├── requirements.txt
├── requirements.yml
├── scripts/
│ ├── start-monitoring.sh
│ ├── setup-control-node.sh
│ ├── use-project-ansible.sh
│ └── verify-monitoring.sh
└── templates/
└── baseline.j2
| Device | Role | IP |
|---|---|---|
| srv-binus | Ansible and monitoring host | 192.168.10.100 |
| rtr-binus-anggrek | CSR1000v router 1 | 192.168.10.10 |
| rtr-binus-alsut | CSR1000v router 2 | 192.168.10.20 |
| rtr-binus-inter | CSR1000v router 3 | 192.168.10.30 |
Run this once on srv-binus from the project root:
cd ~/zodiac
bash scripts/setup-control-node.shActivate the project Ansible environment:
source scripts/use-project-ansible.shAfter activation, ansible-playbook resolves to ~/ztp-cisco-telemetry/.venv-libssh/bin/ansible-playbook, which includes ansible-pylibssh for IOS XE network_cli.
Validate:
which ansible-playbook
ansible --version
ansible-inventory --graph
ansible-playbook playbooks/01_check_reachability.ymlThe same workflow is also available through make:
make setup
make reachabilityThe setup script also installs Docker Engine and Docker Compose plugin. If Docker was just installed or the user was just added to the docker group, open a new shell or use sudo docker ... for the current shell.
Install Docker and Docker Compose plugin on srv-binus:
sudo apt update
sudo apt install -y docker.io docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker "$USER"If a previous Docker Desktop CLI install exists but no Docker daemon is running, install the Ubuntu engine package:
sudo apt install -y docker.io docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker "$USER"The CSR1000v routers must already have management IP, SSH, enable password, and local user access configured from console.
Run all commands from the project root:
cd ~/zodiacValidate inventory:
ansible-inventory --graph
ansible-inventory --listCheck router reachability:
ansible-playbook playbooks/01_check_reachability.ymlBackup running configuration:
ansible-playbook playbooks/02_backup_config.yml
ls -lah backups/Apply baseline configuration:
ansible-playbook playbooks/03_baseline_config.yml
ansible-playbook playbooks/06_save_config.ymlConfigure Loopback0 and OSPF:
ansible-playbook playbooks/04_routing_config.yml
ansible-playbook playbooks/06_save_config.ymlVerify configuration:
ansible-playbook playbooks/05_verify.ymlForce-save running configuration when needed:
ansible-playbook playbooks/06_save_config.ymlStart monitoring:
cd ~/zodiac
make monitoring-up
make monitoring-verifyThe monitoring path is:
CSR1000v SNMP agent -> SNMP Exporter :9116 -> Prometheus :9090 -> Grafana :3000
Service URLs:
| Service | URL |
|---|---|
| SNMP Exporter | http://192.168.10.100:9116 |
| Prometheus | http://192.168.10.100:9090 |
| Grafana | http://192.168.10.100:3000 |
Grafana login:
username: admin
password: admin
Prometheus is automatically provisioned as the Grafana data source, and the Cisco IOS XE SNMP Overview dashboard is loaded from monitoring/grafana/dashboards/cisco-ios-snmp.json.
Router access:
ping -c 3 192.168.10.10
ping -c 3 192.168.10.20
ping -c 3 192.168.10.30
ansible-playbook playbooks/01_check_reachability.ymlRouter configuration:
ansible-playbook playbooks/05_verify.ymlExpected router evidence:
show ip interface brief
show running-config | include snmp-server
show running-config | include ntp server
show running-config interface Loopback0
show ip ospf neighbor
Prometheus targets:
curl http://localhost:9090/-/ready
curl "http://localhost:9116/snmp?target=192.168.10.10&module=if_mib&auth=public_v2"
curl "http://localhost:9090/api/v1/query?query=up%7Bjob%3D%22snmp-cisco-ios%22%7D"Open Prometheus targets:
http://192.168.10.100:9090/targets
Expected state: snmp-cisco-ios targets are UP.
Validated Prometheus API result should include all three router instances with value 1:
192.168.10.10
192.168.10.20
192.168.10.30
Grafana dashboard:
http://192.168.10.100:3000/d/cisco-ios-xe-snmp/cisco-ios-xe-snmp-overview
Expected panels:
- Router Availability:
up{job="snmp-cisco-ios"} - Interface Incoming Traffic:
rate(ifHCInOctets{job="snmp-cisco-ios"}[5m]) * 8 - Interface Outgoing Traffic:
rate(ifHCOutOctets{job="snmp-cisco-ios"}[5m]) * 8 - Interface Operational Status:
ifOperStatus{job="snmp-cisco-ios"} - Router Uptime:
sysUpTime{job="snmp-cisco-ios"} / 100
If Ansible cannot connect, verify SSH from srv-binus:
ssh user@192.168.10.10For a noninteractive credential test:
sshpass -p 'P@ssw0rd' ssh -o StrictHostKeyChecking=no user@192.168.10.10 'show version'If this returns Permission denied, fix the router console bootstrap first. The Ansible inventory and playbooks cannot authenticate until the router has a matching local user and VTY SSH login:
configure terminal
username user privilege 15 secret P@ssw0rd
enable secret P@ssw0rd
ip domain-name binus.local
crypto key generate rsa modulus 2048
ip ssh version 2
line vty 0 4
login local
transport input ssh
privilege level 15
end
write memory
If enable mode fails, confirm enable secret and ansible_become_password match in group_vars/ios_routers.yml.
If playbooks fail with missing modules, install collections:
ansible-galaxy collection install cisco.ios ansible.netcommonIf backup files are missing, confirm the command was run from the project root and that backups/ exists.
If SNMP targets are down in Prometheus, verify router SNMP configuration:
show running-config | include snmp-server
Then test SNMP Exporter directly:
curl "http://localhost:9116/snmp?target=192.168.10.10&module=if_mib&auth=public_v2"If Grafana has no dashboard, restart the stack so provisioning runs again:
cd ~/zodiac/monitoring
docker compose restart grafanaIf OSPF neighbors are empty, verify there is a routed data-plane link between CSR routers. The playbook configures Loopback0 and OSPF process state, but OSPF adjacency requires connected interfaces in the same area.
To fulfill the Implementation Cases Development requirements, a custom Golang HTTP webhook receiver was created. This receiver listens for Grafana alerts when a router goes down (e.g., SNMP targets become unreachable) and logs the alerts to the console.
The receiver is located in cmd/alert-receiver/main.go. To run it locally:
cd ~/zodiac
go run cmd/alert-receiver/main.goThe server will start on port 8080 (or PORT environment variable).
You can simulate a Grafana alert by sending a POST request to the receiver:
curl -X POST http://localhost:8080/webhook \
-H "Content-Type: application/json" \
-d '{
"receiver": "golang-webhook",
"status": "firing",
"alerts": [
{
"status": "firing",
"labels": {
"alertname": "RouterDown",
"instance": "192.168.10.10"
},
"annotations": {
"summary": "Router 192.168.10.10 is DOWN"
}
}
]
}'Grafana is configured with a Contact Point pointing to http://<srv-binus-ip>:8080/webhook to automatically forward alerts to this Golang application.