Using Docker For NAPALM Automation

In the environment I currently work in, the options for scripting servers is not great. Because of access controls in place, any server has to be in a specific subnet and that server is very limited to what operating systems are available to me.

Unfortunately, most of the time it seems any sort of tutorial you’re going to find on network automation and Python programming is going to be Debian-based, usually Ubuntu. These aren’t approved for use in my workplace so I started thinking about how I could utilize Docker to be able to execute scripts on any OS. I’m sure there are plenty of tutorials out there but all the ones I saw were pretty complex and not specific to what I needed. Hopefully this post is a bit easier to follow.

Creating The Docker Image

I’m using the Docker One-Click Application from DigitalOcean for this tutorial. It was the quickest, easiest way to get set up.

The first thing you’ll want to do is create two folders. One called ~/images and one called ~/scripts. Change to the /images directory and create a file called Dockerfile. I personally use the nano editor. Paste the following in, and save the file.

FROM ubuntu:18.04
MAINTAINER Chris Jones (ipv6freely@gmail.com)
RUN apt-get update
RUN apt-get install -y python3-pip
RUN pip3 install napalm
RUN mkdir /scripts
WORKDIR "/scripts"
ENTRYPOINT ["python3"]

Here is what each line of the Dockerfile means:

  • FROM ubuntu:18.04 – This specifies to use Ubuntu 18.04 as the base Docker image
  • MAINTAINER Your Name (yourname@domain.com) – Insert your name and email here
  • RUN apt-get update – Update the package lists
  • RUN apt-get install -y python3-pip – Install PIP for Python3
  • RUN pip3 install napalm – Install NAPALM using pip
  • RUN mkdir /scripts – Create a /scripts directory on the docker image
  • WORKDIR "/scripts" – Instructs Docker to change to the local /scripts directory
  • ENTRYPOINT ["python3"] – Will execute python3 when the Docker image is called from the command line. This can be omitted, but it means you will need to add python3 to the command to execute your scripts at runtime

Next, build the image:

docker build --tag my-napalm:latest .

You should see the Ubuntu docker image being pulled, and your customizations added.

root@docker-01:~/images# docker build --tag my-napalm:latest .
Sending build context to Docker daemon  2.048kB
Step 1/8 : FROM ubuntu:18.04
18.04: Pulling from library/ubuntu
6cf436f81810: Already exists
987088a85b96: Already exists
b4624b3efe06: Already exists
d42beb8ded59: Already exists
Digest: sha256:7a47ccc3bbe8a451b500d2b53104868b46d60ee8f5b35a24b41a86077c650210
Status: Downloaded newer image for ubuntu:18.04
 ---> 47b19964fb50
Step 2/8 : MAINTAINER Your Name (yourname@domain.com)
 ---> Running in 27385ef2decb
Removing intermediate container 27385ef2decb
 ---> 277b53aaa9c5
Step 3/8 : RUN apt-get update
 ---> Running in 15e2b0565b31
Get:1 http://archive.ubuntu.com/ubuntu bionic InRelease [242 kB]
Get:2 http://security.ubuntu.com/ubuntu bionic-security InRelease [88.7 kB]
Get:3 http://security.ubuntu.com/ubuntu bionic-security/multiverse amd64 Packages [3440 B]

<SNIP>

Removing intermediate container c11b25939ddb
 ---> 99ee5a3c06d9
Step 5/8 : RUN pip3 install napalm
 ---> Running in 4cdab684477f
Collecting napalm
  Downloading https://files.pythonhosted.org/packages/c3/9a/36a1283fe678d6cec189db197f4c2ed1c9ea31dc0d2d93b652b42514a332/napalm-2.4.0-py2.py3-none-any.whl (188kB)
Collecting cffi>=1.11.3 (from napalm)
  Downloading https://files.pythonhosted.org/packages/20/f7/87b62a8895bf7c93e907b05b97bc4459c81a38a61151f03a6eae13d863aa/cffi-1.12.2-cp36-cp36m-manylinux1_x86_64.whl (428kB)
Collecting pyIOSXR>=0.53 (from napalm)
  Downloading https://files.pythonhosted.org/packages/fd/86/0f0a45ba27d7c4fb8cf85e09cd2368060f7bb4642ef193edfb8942a027b6/pyIOSXR-0.53.tar.gz
Collecting scp (from napalm)
  Downloading https://files.pythonhosted.org/packages/50/26/bbcf64af02ae5cdde70f10fcac1b955ed6aece5de459c43feeee4d417f20/scp-0.13.0-py2.py3-none-any.whl
Collecting jinja2 (from napalm)
  Downloading https://files.pythonhosted.org/packages/7f/ff/ae64bacdfc95f27a016a7bed8e8686763ba4d277a78ca76f32659220a731/Jinja2-2.10-py2.py3-none-any.whl (126kB)
Collecting pyYAML (from napalm)
  Downloading https://files.pythonhosted.org/packages/9e/a3/1d13970c3f36777c583f136c136f804d70f500168edc1edea6daa7200769/PyYAML-3.13.tar.gz (270kB)
Collecting pyeapi>=0.8.2 (from napalm)
  Downloading https://files.pythonhosted.org/packages/bb/03/58f8bc4ba77ee33fd762fa50915a20304179fa2613827318baf2a08a327e/pyeapi-0.8.2.tar.gz (133kB)
Requirement already satisfied: setuptools>=38.4.0 in /usr/lib/python3/dist-packages (from napalm)
Collecting future (from napalm)
  Downloading https://files.pythonhosted.org/packages/90/52/e20466b85000a181e1e144fd8305caf2cf475e2f9674e797b222f8105f5f/future-0.17.1.tar.gz (829kB)
Collecting textfsm (from napalm)
  Downloading https://files.pythonhosted.org/packages/a1/0d/a1b490503545b3b4600b965eae5d44cc2b6ce27cfb44f4debc563dbb56d3/textfsm-0.4.1.tar.gz
Collecting junos-eznc>=2.2.0 (from napalm)

<SNIP>

Step 6/8 : RUN mkdir /scripts
 ---> Running in 98e39c379ed4
Removing intermediate container 98e39c379ed4
 ---> 9de5ba319920
Step 7/8 : WORKDIR "/scripts"
 ---> Running in b8b97d00af3d
Removing intermediate container b8b97d00af3d
 ---> 580dda614f47
Step 8/8 : ENTRYPOINT ["python3"]
 ---> Running in fd6dabf02201
Removing intermediate container fd6dabf02201
 ---> a1de608e5414
Successfully built a1de608e5414
Successfully tagged my-napalm:latest

Verify the Docker image using the docker images command:

root@docker-01:~/images# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
my-napalm           latest              a1de608e5414        About a minute ago   541MB
ubuntu              18.04               47b19964fb50        3 weeks ago          88.1MB

Create the Python Script

Next, create a simple Python script to test with. I simply named it napalm-docker.py. This script will simply import the NAPALM module, and connect to a router and gather its facts.

Note that I’m using port 2222 to connect. That’s just because I’m using a vSRX that I instantiated using Vagrant on another Digital Ocean droplet.
#!/usr/bin/env python

import napalm

driver = napalm.get_network_driver('junos')

with driver(hostname='107.170.252.233',
            username='root',
            password='Juniper123',
            optional_args={'port': 2222}) as client:

    facts = client.get_facts()

    print(facts)

Run the Script in the Docker Container

Next we use the docker run command to instantiate our container and run our script within it.

docker run -it --rm -v $PWD:/scripts my-napalm napalm-docker.py

Here’s what each part of the command does:

  -i, --interactive                    Keep STDIN open even if not attached
      --rm                             Automatically remove the container when it exits
  -t, --tty                            Allocate a pseudo-TTY
  -v, --volume list                    Bind mount a volume

So, for the -v option, we’re basically telling it to mount the current directory (~/scripts) as /scripts on the container. The -rm option is optional because it is the default, and simply removes the container once it exits. Then we’re feeding the command the name of the container and finally the name of the script.

If all is working correctly, we should see the following:

root@docker-01:~/scripts# docker run -it --rm -v $PWD:/scripts my-napalm napalm-docker.py
{'vendor': 'Juniper', 'model': 'FIREFLY-PERIMETER', 'serial_number': 'ea4dc2a413a3', 'os_version': '12.1X47-D15.4', 'hostname': 'vsrx1', 'fqdn': 'vsrx1', 'uptime': 77699, 'interface_list': ['ge-0/0/0', 'gr-0/0/0', 'ip-0/0/0', 'lsq-0/0/0', 'lt-0/0/0', 'mt-0/0/0', 'sp-0/0/0', '.local.', 'dsc', 'gre', 'ipip', 'irb', 'lo0', 'lsi', 'mtun', 'pimd', 'pime', 'pp0', 'ppd0', 'ppe0', 'st0', 'tap', 'vlan']}

Fantastic! Also if you’re quick enough (and have a second window open) you can use the docker ps command to see the container running.

root@docker-01:~# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
ea01044ef361        my-napalm           "python3 napalm-dockā€¦"   3 seconds ago       Up 2 seconds                            amazing_lalande

Final Thoughts

Obviously the options here are limitless and can get far more complex than what I’ve done. For my own purposes, I can simply build a container that has everything I need, drop it on any server with Docker and be able to run my scripts from there without having to worry about that server’s local environment.

Leave a Reply

Your email address will not be published. Required fields are marked *