Poor Assumptions
Previously on this blog….
In our next session we actually write some code that will take the port config from that Juniper switch and the interface config we created in the previous sessions and install it on a switch using the command runner module of the DNAC API.
Turns out that statement was a mistake based on a poor assumption. This is an assumption that I know better than to make but I have still been burned by multiple times in my career. I assumed just because you can do something easily in one Cisco product (ACI) that it is possible in another Cisco product (DNAC). Turns out that is not the case. Here is a quote from the official DNAC Documentation:
The Command Runner tool allows you to send diagnostic CLI commands to selected devices. Currently, show and other read-only commands are permitted.
Unfortunately, I don’t have access to an ACI playground right now but I did recently have a chance to solve a very similar problem on a totally different platform using Python and an API provided by Palo Alto Networks.
The problem I was faced with was we needed to migrate a Watchguard firewall to a Palo Alto Networks based firewall. The firewall that we were migrating had a bunch of policy, and NAT statements that referred to even more objects. I was able to export those object definitions into a CSV and then write a tiny amount of code that read that CSV and created those objects in the correct Device Group inside of Panorama. You can use a very similar (almost identical) method to add objects directly to a firewall that is not enrolled in Panorama.
Palo Alto Networks deserves big props for having such a well documented API.
The first thing that you need to do is make sure that you have Python and PIP installed on your system. PIP is a package manager that makes it easy to install third party extenions in Python. In our case “panpython” is a third party extension than provides special commands for manipulating Palo Alto firewalls.
After you have PIP installed refer to the Palo Alto Documentation above and install panpython per the provided instructions.
Now it’s time to get to the problem solving. One huge block of objects that I had to import was related to static NATs that were created. This is how I did this easily and quickly.
Sample CSV:
10.1.1.24,192.168.10.101,SERVER1
10.1.1.29,192.168.10.102,SERVER2
10.1.1.26,192.168.10.103,SERVER3
10.1.1.39,192.168.10.104,SERVER4
10.1.1.20,192.168.10.105,SERVER5
10.1.1.22,192.168.10.106,SERVER6
10.1.1.34,192.168.10.107,SERVER7
10.1.1.38,192.168.10.108,SERVER8
10.1.1.49,192.168.10.109,SERVER9
10.1.1.41,192.168.10.110,SERVER10
10.1.1.46,192.168.10.111,SERVER11
10.1.1.59,192.168.10.112,SERVER12
The Python Script:
#!/usr/bin/python3
# This script assumes that there is a CSV file named "addressobjects.csv" in the same
# directory as the script.
# The format of the CSV is:
# OUTSIDEADDRESS,INSIDEADDRESS,SERVERNAME
import getpass #import library that allows us to enter password without echoing text
import csv #import library that allows us to easily parse CSV files
#Import Library and Commands that allow us to manipulate palo alto firewalls.
#Visit https://panos.pan.dev/docs/apis/panpython_qs/ for installation instructions
import panos
from panos import panorama
from panos import objects
from panos import policies
from panos.base import PanObject
from panos.panorama import Panorama
username = input("Enter your username: ")
pwd = getpass.getpass(prompt = 'Enter Panorama Password: ')
#Replace panorama.domain.local with the FQDN or IP Address of your Panorama Server
pano = Panorama('panorama.domain.local', username, pwd)
#Replace DEVICE_GROUP_NAME with the name of the Panorama Device Group The Objects Should be Placed In
devicegroup = panorama.DeviceGroup("DEVICE_GROUP_NAME")
with open('./addressobjects.csv') as addressobjects:
csvReader = csv.reader(addressobjects)
for row in csvReader:
outsideaddr = row[0]
insideaddr = row[1]
name = row[2]
outsidename = name + "_OUTSIDE"
outsidedesc = "NAT to" + insideaddr
insidename = name + "_INSIDE"
insidedesc = "NAT to" + outsideaddr
# Add outside Object
pano.add(devicegroup)
addobject = panos.objects.AddressObject(name=outsidename,value=outsideaddr,description=outsidedesc)
devicegroup.add(addobject)
addobject.create()
# Add Inside Object
pano.add(devicegroup)
addobject = panos.objects.AddressObject(name=insidename,value=insideaddr,description=insidedesc)
devicegroup.add(addobject)
addobject.create()
Using code to take normalized data like the type found in a CSV and converting it to device config is a method I’ve used effectively throughout my career. Hopefully you have found some inspiration or another tool to add to your toolbox here.
In our next series of posts we will continue our programming exercises but instead of practical examples it will focus on using Python to make information on the web accessible to retro computers such as the Commodore Amiga or 128, Tandy Model 102, and Mac SE. (Or any other type of computer with a terminal program and RS-232 interface.)