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 imageMAINTAINER Your Name (yourname@domain.com)
– Insert your name and email hereRUN apt-get update
– Update the package listsRUN apt-get install -y python3-pip
– Install PIP for Python3RUN pip3 install napalm
– Install NAPALM using pipRUN mkdir /scripts
– Create a /scripts directory on the docker imageWORKDIR "/scripts"
– Instructs Docker to change to the local /scripts directoryENTRYPOINT ["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 addpython3
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.
#!/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.