Backing up and tracking device configs with Ansible & Git

It seems reasonable to backup our devices config on a daily basis, or even better, after every change window, as well as to have some sort of revision control so that we know what changes were made and at what time.

In this post, I’m going to explain the process required to get this up and running using Ansible and Github.

Keep in mind that if you want to implement this in production, you probably don’t want your devices’ config on a public repository on Github. I’d recommend you to spin up your own Git server internally or pay for the private repository plan on Github/Bitbucket/Gitlab.

There are four main steps that we need to follow:

  1. Create our Ansible playbook to take a backup of our devices’ config
  2. Parse the Ansible output to a human-readable text file
  3. Push the config files to Github
  4. Automate the process of taking backups and pushing them to Github

Create our Ansible playbook to take a backup of our devices’ config

First of all, we need to define the devices we want to back up, as well as the network operating system they’re running. In this example the devices I’m using are running Arista EOS; this can be very easily extensible to any NOS supported by Ansible – full list available here.

The above-mentioned details will populate the Ansible inventory file, which in our case, will look as follows:


[all:vars]
ansible_connection=local
ansible_python_interpreter=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3

[arista]
sw1 ansible_host=172.16.155.111
sw2 ansible_host=172.16.155.112
sw3 ansible_host=172.16.155.113
sw4 ansible_host=172.16.155.114

[arista:vars]
username=ismael
password=ismael

Secondly, we need to create the actual playbook as well as our group_vars:


---

- name: Get running config
  hosts: arista
  gather_facts: false

tasks:
- name: Arista show running-config using SSH
  eos_command:
    provider: "{{ conn_ssh }}"
    commands: show running-config
    authorize: yes # don't run enable mode commands until it's on enable mode
  register: show_run

- name: Print output of the show command
  debug:
    msg: "{{ show_run.stdout_lines[0] }}"

- name: Copy output to a txt file
  copy:
    content: "{{ show_run.stdout_lines[0] }}"
    dest: "/Users/ismael/Desktop/PycharmProjects/BLOG/www.iserghini.com/Backup_configs/sw_configs/{{ inventory_hostname }}_running_config.txt"

Parse the Ansible output to a human-readable text file

Ansible dumps the output of the show running-config into a file inside a list of strings. This output is not natural for a human to read. Hence I’m using a simple python script to open each config file inside the folder sw_configs, iterate over the lists and create new config files with a CLI-alike format.

Ansible file output:


["! Command: show running-config", "! device: sw1 (vEOS, EOS-4.18.5M)", "!", "! boot system flash:/vEOS-lab.swi", "!", "transceiver qsfp default-mode 4x10G", "!", "hostname sw1", "!",.....

File output after being parsed:


! Command: show running-config
! device: sw1 (vEOS, EOS-4.18.5M)
!
! boot system flash:/vEOS-lab.swi
!
transceiver qsfp default-mode 4x10G
!
hostname sw1
!
spanning-tree mode mstp
!
no aaa root
!

.....

Python script:


#!/Library/Frameworks/Python.framework/Versions/3.6/bin/python3.6

import json
import os

for filename in os.listdir("./sw_configs/"):
    with open("./sw_configs/"+filename) as f:
    file = f.read()

file_json = json.loads(file)

with open("./sw_configs/"+filename, "w") as f:
    for line in file_json:
    f.write(line)
    f.write("\n")

Push the config files to Github

Assuming that the git repository has already been initialised as well as the remote repository URL, and username/password configured, whenever a change is made, we’ll need to execute the following commands.


git add sw_configs/*
git commit -m "backup config"
git push

By now, you may be thinking…wow this looks great, but it seems a real pain having to run the Ansible playbook + the Python script + the git commands manually each time I want to take some backups. You know what? I totally get it, that’s where the following section comes into play:

Automate the process of making backups and pushing them to Github

There are two things to take into account here.

Running all commands together. This is going to be achieved using the bellow bash script:

 

#!/bin/bash

ansible-playbook backup_sw_config.yml -i hosts
./format_running_config.py

git add sw_configs/*
git commit -m "backup config"
git push

Automatically run the above script. In order to do so, I’m going to use the below CRON job, which will take a backup every day of the week at 23:00.


vagrant@precise64:~$ crontab -l | grep "0 23"</span>

0 23 * * * ./git_sw_backup.sh

*I’d highly recommend you to use the full path for the bash script and the crontab.

 

Below you can find a practical example of what was discussed along this post.

I’ve made a couple of changes on SW3 and SW4. After which I’ve run the Bash script – you can see a capture on Github with the diff:

SW3 changes:


sw3#sh run | s router bgp

router bgp 65413

 neighbor 172.16.155.111 remote-as 65411

 neighbor 172.16.155.111 maximum-routes 12000

 neighbor 172.16.155.112 remote-as 65412

 neighbor 172.16.155.112 maximum-routes 12000

 neighbor 172.16.155.114 remote-as 65414

 neighbor 172.16.155.114 maximum-routes 12000

 network 3.3.3.3/32

sw3#conf t

sw3(config)#router bgp 65413

sw3(config-router-bgp)#network 33.33.33.33/32

sw3(config-router-bgp)#int lo2

sw3(config-if-Lo2)#ip add 33.33.33.33/32

sw3(config-if-Lo2)#no shut

sw3(config-if-Lo2)#end

sw3#wr

Copy completed successfully.

sw3#Connection to 172.16.155.113 closed.

SW4 changes:


sw4#conf t

sw4(config)#router bgp 65414

sw4(config-router-bgp)#no neighbor 172.16.155.113 shutdown

sw4(config-router-bgp)#sh run | s router bgp

router bgp 65414

 neighbor 172.16.155.111 remote-as 65411

 neighbor 172.16.155.111 maximum-routes 12000

 neighbor 172.16.155.112 remote-as 65412

 neighbor 172.16.155.112 maximum-routes 12000

 neighbor 172.16.155.113 remote-as 65413

 neighbor 172.16.155.113 maximum-routes 12000

 network 4.4.4.4/32

sw4(config-router-bgp)#end

sw4#wr

Copy completed successfully.

Here we can see the diff

Screen Shot 2018-07-08 at 21.22.28

Please feel free to pop into my Github repository if you want the full code (with the correct indentation) at Backup_configs.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s