This repository contains Ansible-based automation for deploying OSPF (Open Shortest Path First) dynamic routing configurations on Cisco routers in lab environments. All projects target Cisco IOS devices (vIOS, IOSv) managed via SSH.
- 3-Router OSPF Design (
ospf-lab/): Simple 3-router topology with 2 OSPF areas - multi-site-ospf/: Production-grade 6-router setup with phased deployments
- ospf-lab-6r/: Advanced 6-router lab with HQ/Branch structure and ABR (Area Border Router) roles
- Cisco vIOS Day 0 Template: Base connectivity verification template
- Inventory Layer (
inventory/hosts.yml): Defines router groups (hq_routers, branch_routers, routers) - Variable Layer (
inventory/group_vars/,host_vars/): Stores interface and OSPF configs per router/group - Role Layer (
roles/): Two core roles -base_interfaces(layer 3 setup) andospf(routing config) - Template Layer (Jinja2): Generates actual IOS CLI commands from variables
- Execution Layer (
playbooks/): Orchestrates roles with handlers and validation tasks
Configuration is entirely variable-driven. Router-specific host vars define IP addresses, interface names, and OSPF parameters. Templates render these into CLI commands. This allows one playbook to deploy across different topologies by changing only inventory/vars files.
Location: roles/base_interfaces/
# In host_vars/R3.yml:
interfaces:
- name: Ethernet0/1
description: "TO_R6_BRANCH_AREA1"
ipv4_address: 10.1.1.1
ipv4_netmask: 255.255.255.252
# Template renders to: interface Ethernet0/1 → description → ip addressPattern: Define interfaces as structured list in host_vars. base_interfaces/tasks/main.yml uses cisco.ios.ios_config with template source to apply all at once.
Location: roles/ospf/
Variables define network statements as list of dicts with network, wildcard, and area. Template iterates:
{% for network in ospf_networks %}
network {{ network.network }} {{ network.wildcard }} area {{ network.area }}
{% endfor %}Critical: Passive interfaces declared separately. ABR routers require network statements in BOTH areas they connect.
Location: multi-site-ospf/ and ospf-lab-6r/
phase1_deploy.yml: Deploys base_interfaces + OSPF on all routers simultaneouslyphase2_abr_deploy.yml(in multi-site-ospf): Optional second phase for ABR tuningverify_ospf.yml: Runs show commands to verify neighbors, routes, database
Pattern: Use handlers with notify: save config to persist configs only when changes occur. Include extensive ios_command tasks with retries to wait for OSPF convergence (typically 15-30 seconds).
- hq_routers: Core routers in Area 0 (R3, R4, R5 in ospf-lab-6r)
- branch_routers: Edge routers in Area 1 (R6, R7, R8)
- routers: Combines both for single-playbook deployment
Use ansible_host for management IP (e.g., 192.168.33.x). This is separate from data plane IPs defined in interfaces.
- Connection type: Implicit (defaults to ssh for Cisco IOS)
gathering = explicit: Only gather facts when explicitly requested to save timetimeout = 60,connect_timeout = 60: Necessary for initial router connectionshost_key_checking = False: Lab environment bypass
# Deploy all OSPF config (both interface and routing)
ansible-playbook playbooks/deploy_ospf.yml
# Deploy only interfaces (for debugging)
ansible-playbook playbooks/deploy_ospf.yml --tags interfaces
# Deploy only OSPF config
ansible-playbook playbooks/deploy_ospf.yml --tags ospf
# Verify after deployment
ansible-playbook playbooks/verify_ospf.ymlansible-playbook playbooks/rollback.ymlAlways include 15-30 second pause after OSPF config and use retries/until on show commands:
- name: Wait for OSPF to converge
pause:
seconds: 15
prompt: "Waiting for OSPF adjacencies to form..."
- name: Verify OSPF neighbors
cisco.ios.ios_command:
commands: ["show ip ospf neighbor"]
register: ospf_neighbors
retries: 3
delay: 5
until: ospf_neighbors is succeededUse ios_config with src: parameter pointing to Jinja2 template. This is cleaner than multiple ios_command tasks for complex configs:
- name: Deploy OSPF from template
cisco.ios.ios_config:
src: ospf.j2
notify: save configAvoid full ios_facts in production playbooks (slow). Use targeted commands:
- name: Gather device facts (minimal)
cisco.ios.ios_facts:
gather_subset: minansible.cfg: Connection settings (all projects)inventory/hosts.yml: Router IP addresses and groupinginventory/group_vars/all.yml: Variables applied to all routers (ospf_process_id, ospf_reference_bandwidth)inventory/group_vars/routers.yml: Router group defaultsinventory/host_vars/R*.yml: Per-router interfaces and OSPF networks
roles/base_interfaces/templates/interfaces.j2: Generates interface configsroles/ospf/templates/ospf.j2: Generates router ospf process configroles/ospf/defaults/main.yml: OSPF role defaults (retry counts, timeouts)
playbooks/deploy_ospf.yml: Main deployment (runs base_interfaces then ospf roles)playbooks/verify_ospf.yml: Post-deployment verificationplaybooks/rollback.yml: Config restore (ospf-lab-6r only)
- Verify
ansible_hostmatches actual management IP in inventory - Check
sshaccess:ssh ansible@192.168.33.xxxfrom control node - Review ansible.cfg timeout settings if connections drop
- Templates reference variables like
ospf_router_id,ospf_networks. Missing these causes render failures - Check
host_varsfor required keys in ospf_networks dicts:network,wildcard,area
- Run
verify_ospf.ymlto see neighbor relationships - Check that interfaces are in correct OSPF areas (network statements must match)
- Verify passive interfaces don't prevent needed adjacencies
- Ensure loopback0 addresses match ospf_router_id
- Handlers only save if
ios_configtask haschanged: true - Use
notify: save configon all configuration tasks to ensure persistence
- All comments in configs use
!(IOS standard), no C-style comments - Router IDs set to loopback0 primary address (e.g., R3 = 3.3.3.3)
- Point-to-point links use /30 subnets (4 hosts, minimal waste)
- Area 0 always contains backbone/core routers, Area 1 for branches (follows OSPF best practices)
- Variable names use underscores:
ospf_router_id, notospfRouterId