Network Infrastructure As Code With Ansible & Git – Part 2

In part one we learned how to use Ansible and a data model to represent infrastructure as code. Now it’s time to introduce Git as the central network automation tool to use the advantages that result from working with text files.

I cannot emphasize enough how important this step is to long-term success with an automation initiative. Unversioned files with funny names in a random directory are not the solution.

Prerequisites

Git is a software to realize a distributed version control system and needs to be installed on the Ansible control node.

$ sudo yum install git

It is best practice to set up personal information that will be used when changes are committed to the repository.

$ git config --global user.name "nwmichl" 
$ git config --global user.email "mail@nwmichl.net"

Version Control With Git

Now back to the syslog configuration example. A new directory named iac for this little InfrastructureAsCode demo includes the two files we developed in the previous blog post.

[nwmichl@localhost ~]$ tree iac/
iac/
├── group_vars.yml
└── syslog.yml

As I mentioned before, versioning via file name, like syslog_v1a.yml, is not the right approach. To use git for this task we just need to put our directory under version control,

$ cd ~/iac
$ git init
Initialized empty Git repository in /home/nwmichl/iac/.git/

tell git about all the file we would like to be tracked,

$ git add .
$ git status
# On branch master
#
# Initial commit
#
# Changes to be commited:
#         new file:    group_vars.yml
#         new file:    syslog.yml

and commit the actual state of those files to the central master branch of the repository. Branches enable further development independant of production code (in the master branch), but we don’t need them at first.

$ git commit -m "Initial commit"
[master (root-commit) 668a138] Initial commit
 2 files changed, 45 insertions(+)
 create mode 100644 group_vars.yml
 create mode 100644 syslog.yml
$ git status
# On branch master
nothing to commit, working directory clean

Wow, I know, that all looks weird the first time and if you search the web for Git tutorials you might be overwhelmed by the possibilities. But believe me, for the purpose of InfraAsCode we only need a very small subset of functions and you will definitely get used to it.

So to recap: With the first three commands git init, git add . and git commit we put the directory iac under version control and Git now takes care about changes using the new special folder ~/iac/.git .

Tracking changes

Let’s assume I want to change the syslog.yml playbook, maybe a comment needs improvement. I’d vi syslog.yml and store the new version via :wq. The filename didn’t change, of cause, but Git knows that something happend:

[nwmichl@localhost iac]$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#       modified:   syslog.yml
#
no changes added to commit (use "git add" and/or "git commit -a")

You can also compare all the changes made since the last commit to the repository line by line, where - indicates a deletion and + the new substitute.

$ git diff
diff --git a/syslog.yml b/syslog.yml
index 45b432e..4a4e529 100644
--- a/syslog.yml
+++ b/syslog.yml
@@ -1,4 +1,4 @@
-# Playbook realizing declarative syslog configuration
+# Playbook realizing declarative syslog configuration with Cisco IOS devices

Because I like what I did there, I commit to a new version of this repo.

$ git add .
$ git commit -m "Changed syslog.yml comment"
[master 48c60ab] Changed syslog.yml comment
 1 file changed, 1 insertion(+), 1 deletion(-)

Git assigns a unique hash value to each version, 48c60ab in this case, to differentiate between them and curates a list of all commits since the initialisation of the repository. Visit git log to get verbose information of the whole git history.

$ git log --oneline
48c60ab Changed syslog.yml comment
668a138 Initial commit

BTW: Please do yourself and everyone working with your repositories a favour and always use meaningful commit messages! 😉

If you now would like to compare two commits and see all the differences, just use the git diff command again, this time specifying the two hashes.

$ git diff 48c60ab 668a138
diff --git a/syslog.yml b/syslog.yml
index 4a4e529..45b432e 100644
--- a/syslog.yml
+++ b/syslog.yml
@@ -1,4 +1,4 @@
-# Playbook realizing declarative syslog configuration with Cisco IOS devices
+# Playbook realizing declarative syslog configuration

That’s enough to grasp the relevant concept behind git, but it certainly doesn’t hurt to dig a little deeper at times.

GitLab as a Central Repository

Let’s consider the challenge of versioning as solved for now.
The other problem is, that we are only working with a repository local to the Ansible control node and even worse just in the home directory of one user (nwmichl). As most of us are part of a team, we need a way to centrally store all repositories of our automation stack and work collaboratively. This is where hosting platforms like GitHub or GitLab come to the party.

Both solutions provide (free) private projects (GitLab) / repositories (GitHub) or even on-prem installation which is great, because most of us would feel uncomfortable with public hosting of their automation environment, right? For the sake of this blog it will be GitLab, and I use an account for the user NWMichl.

Initial push

There is no need to create the new private GitLab project using the Web-UI, just push the local repository like this:

$ git push --set-upstream https://gitlab.com/NWMichl/iac.git master
Username for 'https://gitlab.com': nwmichl
Password for 'https://nwmichl@gitlab.com':
Counting objects: 7, done.
Compressing objects: 100% (7/7), done.
Writing objects: 100% (7/7), 1014 bytes | 0 bytes/s, done.
Total 7 (delta 1), reused 0 (delta 0)
remote:
remote: The private project NWMichl/iac was successfully created.
remote:
remote: To configure the remote, run:
remote:   git remote add origin https://gitlab.com/NWMichl/iac.git
remote:
remote: To view the project, visit:
remote:   https://gitlab.com/NWMichl/iac
remote:
To https://gitlab.com/NWMichl/iac.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from https://gitlab.com/NWMichl/iac.git.

Et voila, a new private project named iac will automagically appear at GitLab. All files are there and, even more important, the complete change history (2 commits).

(Very) Basic Change Workflow

The following example demonstrates how developers usually work together in a collaborative fashion using hosting platforms like GitLab.

Imagine John Ops from the operations department needs to add another syslog receiver because IT security decided it’s time to use a full blown SIEM. He recently followed some cool NetDevOps advocates on Twitter and thus decides to skip the ITIL based service request workflow via ticket and opens an GitLab issue instead, wicked! Because the iac project is private, he needs access with at least guest permissions, configured under the members section by a project maintainer.

A guest has no access to the repository itself, but can open new issues.

A short time later a notification about a the new issue pops up in the networking teams slack (yes, that’s possible!) and the project owner himself takes care of this task. As he wiped the whole project from his home directory since it is hosted at the central GitLab repository, he first has to download a copy.

$ git clone https://gitlab.com/nwmichl/iac.git
Cloning into 'iac'…
Username for 'https://gitlab.com': nwmichl
Password for 'https://nwmichl@gitlab.com':
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 7 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (7/7), done.
$ cd iac/

Now the new syslog receiver according to the GitLab issue has to be added to the group_vars.yml file, via vim for instance.

syslog_server:
 192.168.10.12
 10.10.10.10
 1.1.1.1

Next, he executes the syslog.yml playbook once again to configure the new syslog server on all switches.

The play recap looks great, so the GitLab issue can be closed.

But wait, the GitLab repository doesn’t reflect the changes made, the group_vars.yml still misses the new 1.1.1.1 server:

Sure, we’ve forgotten we’re only working with a local copy. So the change has to be committed to the local repository at the Ansible control node to create a new version and pushed to the GitLab repository.

$ git add .
$ git commit -m "Add new syslog receiver 1.1.1.1"
[master 65f19ca] Add new syslog receiver 1.1.1.1
  2 files changed, 1 insertion(+), 3 deletions(-)
$ git push origin master
Username for 'https://gitlab.com': nwmichl
Password for 'https://nwmichl@gitlab.com':
Counting objects: 7, done.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 373 bytes | 0 bytes/s, done.
Total 4 (delta 1), reused 0 (delta 0)
To https://gitlab.com/nwmichl/iac.git
   48c60ab..65f19ca  master -> master

That looks better now and the change workflow has finished.

Closing

We learned how to use Git as a version control system and a central repository like GitLab, including a first little glimpse of the collaborative possibilities.

Do you think the configuration part included way too many manual interactions for a network automation solution? Right, that was only for didactic reasons! But relax, in the next blog post I’ll close the loop and show how to fully automate the process, including some testing.

One thought on “Network Infrastructure As Code With Ansible & Git – Part 2

  1. Very simple and informative description for beginners and those who looks for how to replace legacy workflow with a modern one.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.