Monday, 18 July 2016

Python for Network Engineers - Part 7 - Using Palo Alto Networks XML API

In this blog article we’re going to look at using the Python requests module to interface with the Palo Alto Networks (PAN-OS) XML API.

Blog Series

Python for Network Engineers - Part 1 - Introduction
Python for Network Engineers - Part 2 - Making REST calls
Python for Network Engineers - Part 3 - Using Cisco Nexus NX-API
Python for Network Engineers - Part 4 - Using Arista EOS eAPI
Python for Network Engineers - Part 5 - Using Junos NETCONF interface
Python for Network Engineers - Part 6 - Using Cisco Nexus NETCONF interface
Python for Network Engineers - Part 7 - Using Palo Alto Networks XML API


Introduction

In previous blog posts, we gave an introduction to using the requests module to interact with REST APIs and also looked at the ncclient module for NETCONF interfaces.

In this blog post, we’ll use the requests module again to interact with the Palo Alto XML API.  It’s a little harder to get started with than examples in previous blog posts.  But once you’ve worked through a few examples it’s fairly straightforward.

It’s also a little unusual as we are just using HTTP GET requests and using XML data in the query section of the URI string.  So, in this case, it’s not a true REST API, but as we're using the requests module again, much of the knowledge picked up in parts 2, 3 and 4 will be helpful here too..

Palo Alto XML API

For this example, I’m using a VM-Series Palo Alto firewall with PAN-OS 6.1.0 and Ubuntu 16.04 linux.

PAN Setup
On the Palo Alto firewall then there is no setup required to enable the XML API.  We just need to have web browser access to the firewall.

However, before we start trying to query or configure the firewall we will need to generate an authentication key.  We can do this from Python using the requests module.

Key Generation
First open an interactive Python session and we will setup all the basic settings and create a session object “s” to the firewall.
import requests, json, xmltodict
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
uri = 'https://192.168.229.71/api/'
s = requests.session()
s.verify = False

The second and third lines will just disable some SSL warnings which will make the output harder to read.  Next we will send a request to the firewall for a key to be created for the below user “admin”.
uname = upass = "admin"
params = {}
params['type'] = 'keygen'
params['user'] = uname
params['password'] = upass
r = s.request('get', uri, params=params)

We can then take a look at the key that was sent back and can also set it into a string called “key” which we will use in the further examples below
print r.text
d = xmltodict.parse(r.text)
key = d['response']['result']['key']
print key

Example 1
In this example we’ll just do a few basic show commands.  This assumes that you’ve already created your session object “s” and you have set a string called “key” with the correct authentication key.
params = {'type': 'config', 'action': 'show', 'key': key}
params['xpath'] = '/config/mgt-config'
r = s.get(uri, params=params)
print r.text

One thing to note is that I've switched to using "s.get()" syntax.  Above and in previous blog posts I've used "s.request("get") as it's easy to switch to using "post" or "put".  However as we'll only be using "get" with PAN then we'll use the shorter command.

This next query will list all the address objects on the firewall
params['xpath'] = '/config/devices/entry/vsys/entry/address'
r = s.get(uri, params=params)
print r.text

This next query will list the security and NAT rulebases on the firewall
params['xpath'] = '/config/devices/entry/vsys/entry/rulebase'
r = s.get(uri, params=params)
print r.text

Example 2
In this next example, we’ll show a fairly simple configuration item so we can demonstrate the commit process.  Again we assume that you’ve already created your session object “s” and you have set a string called “key” with the correct authentication key.

Now we will create an address object :
params = {}
params['type'] = 'config'
params['action'] = 'set'
params['key'] = key
params['xpath'] = "/config/devices/entry/vsys/entry/address/entry[@name='HST_10.1.1.1']"
params['element'] = '<ip-netmask>10.1.1.1/32</ip-netmask>'
params['element'] += '<description>Test Internal Host IP - added by python</description>'
r = s.get(uri, params=params)
print r.text

And then commit the configuration:
params = {}
params['type'] = 'commit'
params['cmd'] = '<commit></commit>'
params['key'] = key
r = s.get(uri, params=params)

However, if we now check the output from the reply object, it only tells us a job id and not if it completed OK.
print r.text

So to check the job completed we can run the following (example is where job id id 99):
jobid = 99
params = {}
params['type'] = 'op'
params['cmd'] = '<show><jobs><id>%s</id></jobs></show>' % jobid
params['key'] = key
r = s.get(uri, params=params)
print r.text

Conclusion

I’ve only shown a couple of examples here as we basically need to build the XML for each type of request and understand the xpath for each task that you want to do.  Also if you’re using VSYS or if you’re using “shared” objects then the xpath will change for certain tasks.  Where I’ve used a virtual Palo Alto in this blog, it’s made the xpath format a certain way as there’s no VSYS and no shared objects.

To use this in production then I have developed a module that does all the heavy lifting such as doing commits, checking the job id result, storing all the outputs in class parameters, logging and reporting back with a Boolean True/False for each command run.  This makes it very easy to script changes on top of that and debug when things aren’t working.

Also please refer to the documentation below to build on top the examples shown in this blog.

Resources



About the Author

The author of this blog works for Vanguard IT who provide a range of professional services and managed services

For more information go to https://vanguard-it.net

No comments:

Post a Comment