In this post we will automate the deployment of a Cisco DMVPN network. I use this as a lab for testing, but with a few tweaks you could use this to deploy a production DMVPN network. I’ve tested this in GNS3 on IOSv 15.6 routers and the Network Automation appliance from the GNS3 marketplace, with Ansible v2.5 installed. The DMVPN network is dual-hub, with 3 spokes, using EIGRP as the WAN routing protocol, and based on this Cisco validated design. You can find the all code in my Git repo.
As per best practice we are using Ansible Roles to break the deployment up into smaller chunks. The roles are as follows:
- acl
- crypto
- interfaces
- hubs
- spokes
- test
The ‘ACL’ role applies an access control list to the WAN interface. ‘Crypto’ takes care of the Tunnels, ‘Interfaces’ looks after the interfaces, and the ‘hub’ and ‘spoke’ roles handle configuration that is specific to the device role. The ‘test’ role performs basic ping tests from each router to all of the loopback addresses in the topology. All these roles are called by a single playbook that provisions the whole network, and then tests connectivity. Here’s our playbook:
root@netdev1:~/ansible-dmvpn# cat deploy_dmvpn.yml --- - name: PLAY 1 - deploy common configuration hosts: all connection: local gather_facts: 'no' roles: - role: acl tags: acl - role: crypto tags: crypto - role: interfaces tags: interfaces - name: PLAY 2 - deploy hub configuration hosts: hubs connection: local gather_facts: 'no' roles: - role: hubs tags: hubs - name: PLAY 3 - deploy spoke configuration hosts: spokes connection: local gather_facts: 'no' roles: - role: spokes tags: spokes - name: PLAY 4 - test connectivity hosts: all connection: local gather_facts: 'no' roles: - role: test tags: test
The playbook is run against the inventory file, which looks like this:
root@netdev1:~/ansible-dmvpn# cat inventory [all:vars] #SSH connection ansible_network_os=ios ansible_user=ansible ansible_ssh_pass=ansible #Tunnel PSK pre_shared_key=cisco123 [hubs] hub1 ansible_host=192.168.122.100 hub2 ansible_host=192.168.122.101 [spokes] spoke1 ansible_host=192.168.122.102 spoke2 ansible_host=192.168.122.103 spoke3 ansible_host=192.168.122.104
Before we can use Ansible to connect to the routers, we have to apply a minimal config to bring up the WAN interface in a ‘front door’ VRF and create the Ansible user account. Now we can run our playbook:
root@netdev1:~/ansible-dmvpn# ansible-playbook -i inventory deploy_dmvpn.yml PLAY [PLAY 1 - deploy common configuration] ************************************ TASK [acl : deploy acl configuration] ****************************************** changed: [spoke3] changed: [hub2] changed: [spoke2] changed: [spoke1] changed: [hub1] TASK [crypto : deploy crypto configuration] ************************************ changed: [hub2] changed: [spoke2] changed: [spoke1] changed: [hub1] changed: [spoke3] TASK [interfaces : deploy interface configuration] ***************************** changed: [spoke2] changed: [hub1] changed: [spoke3] changed: [spoke1] changed: [hub2] PLAY [PLAY 2 - deploy hub configuration] *************************************** TASK [hubs : deploy hub specific configuration] ******************************** changed: [hub2] changed: [hub1] PLAY [PLAY 3 - deploy spoke configuration] ************************************* TASK [spokes : deploy spoke specific configuration] **************************** changed: [spoke1] changed: [spoke2] changed: [spoke3] PLAY [PLAY 4 - test connectivity] ********************************************** TASK [test : quick test to ping all loopback IP's] ***************************** ok: [hub1] => (item=192.168.123.100) ok: [hub2] => (item=192.168.123.100) ok: [spoke2] => (item=192.168.123.100) ok: [spoke1] => (item=192.168.123.100) ok: [spoke3] => (item=192.168.123.100) ok: [hub1] => (item=192.168.123.101) ok: [hub2] => (item=192.168.123.101) ok: [spoke1] => (item=192.168.123.101) ok: [spoke2] => (item=192.168.123.101) ok: [spoke3] => (item=192.168.123.101) ok: [hub1] => (item=192.168.123.102) ok: [hub2] => (item=192.168.123.102) ok: [spoke1] => (item=192.168.123.102) ok: [spoke2] => (item=192.168.123.102) ok: [spoke3] => (item=192.168.123.102) ok: [hub1] => (item=192.168.123.103) ok: [spoke1] => (item=192.168.123.103) ok: [hub2] => (item=192.168.123.103) ok: [spoke2] => (item=192.168.123.103) ok: [spoke3] => (item=192.168.123.103) ok: [hub1] => (item=192.168.123.104) ok: [spoke1] => (item=192.168.123.104) ok: [hub2] => (item=192.168.123.104) ok: [spoke2] => (item=192.168.123.104) ok: [spoke3] => (item=192.168.123.104) PLAY RECAP ********************************************************************* hub1 : ok=5 changed=4 unreachable=0 failed=0 hub2 : ok=5 changed=4 unreachable=0 failed=0 spoke1 : ok=5 changed=4 unreachable=0 failed=0 spoke2 : ok=5 changed=4 unreachable=0 failed=0 spoke3 : ok=5 changed=4 unreachable=0 failed=0
So, our basic ping test in PLAY 4 proves that the DMVPN is up and running, but if we want to check in more detail we can run another playbook for this and limit the run to only a specific router or subset of routers:
root@netdev1:~/ansible-dmvpn# ansible-playbook -i inventory verify_dmvpn.yml --limit spoke3 PLAY [VERIFY DMVPN] ************************************************************ TASK [CHECK TUNNEL INT | EIGRP | NHRP | DMVPN | IPSEC] ************************* ok: [spoke3] TASK [debug] ******************************************************************* ok: [spoke3] => { "print_output.stdout_lines": [ [ "Tunnel10 10.0.0.104 YES manual up up" ], [ "EIGRP-IPv4 VR(WAN-DMVPN-1) Address-Family Neighbors for AS(10)", "H Address Interface Hold Uptime SRTT RTO Q Seq", " (sec) (ms) Cnt Num", "0 10.0.0.101 Tu10 54 00:06:56 1378 5000 0 23", "1 10.0.0.100 Tu10 54 00:07:17 66 396 0 12" ], [ "IP routing table name is default (0x0)", "IP routing table maximum-paths is 32", "Route Source Networks Subnets Replicates Overhead Memory (bytes)", "connected 0 3 0 204 540", "static 0 0 0 0 0", "application 0 0 0 0 0", "nhrp 0 2 0 136 360", "eigrp 10 0 4 0 688 720", "internal 2 880", "Total 2 9 0 1028 2500" ], [ "10.0.0.100/32 via 10.0.0.100", " Tunnel10 created 00:07:24, never expire ", " Type: static, Flags: used ", " NBMA address: 192.168.122.100 ", "10.0.0.101/32 via 10.0.0.101", " Tunnel10 created 00:07:23, never expire ", " Type: static, Flags: used ", " NBMA address: 192.168.122.101 ", "10.0.0.102/32 via 10.0.0.102", " Tunnel10 created 00:07:07, expire 00:02:53", " Type: dynamic, Flags: router used nhop rib ", " NBMA address: 192.168.122.102 ", "10.0.0.103/32 via 10.0.0.103", " Tunnel10 created 00:07:04, expire 00:02:56", " Type: dynamic, Flags: router used nhop rib ", " NBMA address: 192.168.122.103 ", "10.0.0.104/32 via 10.0.0.104", " Tunnel10 created 00:07:06, expire 00:02:56", " Type: dynamic, Flags: router unique local ", " NBMA address: 192.168.122.104 ", " (no-socket) ", "192.168.123.101/32 via 10.0.0.101", " Tunnel10 created 00:07:09, expire 00:02:49", " Type: dynamic, Flags: router used rib nho ", " NBMA address: 192.168.122.101 ", "192.168.123.102/32 via 10.0.0.102", " Tunnel10 created 00:07:07, expire 00:02:53", " Type: dynamic, Flags: router rib nho ", " NBMA address: 192.168.122.102 ", "192.168.123.103/32 via 10.0.0.103", " Tunnel10 created 00:07:04, expire 00:02:56", " Type: dynamic, Flags: router rib nho ", " NBMA address: 192.168.122.103 ", "192.168.123.104/32 via 10.0.0.104", " Tunnel10 created 00:07:02, expire 00:02:58", " Type: dynamic, Flags: router unique local ", " NBMA address: 192.168.122.104 ", " (no-socket)" ], [ "Interface: Tunnel10, IPv4 NHRP Details ", "Type:Spoke, NHRP Peers:4, ", "", " # Ent Peer NBMA Addr Peer Tunnel Add State UpDn Tm Attrb", " ----- --------------- --------------- ----- -------- -----", " 1 192.168.122.100 10.0.0.100 UP 00:07:22 S", " 2 192.168.122.101 10.0.0.101 UP 00:07:21 S", " 10.0.0.101 UP 00:07:23 DT2", " 2 192.168.122.102 10.0.0.102 UP 00:07:06 DT1", " 10.0.0.102 UP 00:07:06 DT2", " 2 192.168.122.103 10.0.0.103 UP 00:07:03 DT1", " 10.0.0.103 UP 00:07:03 DT2" ], [ "Active ISAKMP SA's: 6", "Standby ISAKMP SA's: 0", "Currently being negotiated ISAKMP SA's: 0", "Dead ISAKMP SA's: 0" ], [ "IPsec SA total: 16, active: 8, rekeying: 0, unused: 8, invalid: 0" ] ] } PLAY RECAP ********************************************************************* spoke3 : ok=2 changed=0 unreachable=0 failed=0
I was inspired to write this blog post after reading some of the awesome content on https://techbloc.net/ and https://networkautomationblog.com/
Nice post Rich! Keep automating! Thanks for the shout out of my website networkautomationblog.com
Thanks Ken! You’re very welcome – you have a great site!
Rich
Thank you very much for this wonderful website.
Thank you for the comment
Can I see the network topology you used please?
I don’t have a topology diagram but
The DMVPN network is dual-hub, with 3 spokes