I come from a network engineering background, so when I started my network automation journey, it felt like I was banging my head against new technology with very little help. When I first started using APIs, our network team had just started with this brand new ACI “thing,” and I couldn’t find a well written “What is an API” blog with code I would call “appropriate.” In this blog, I will attempt to provide that for those of you that are just getting started with APIs. If you have been using APIs for a while, this may not be as useful.
One of the hardest parts of writing this was choosing an API to discuss. Some of the things I really wanted from an API were:
Sadly, this combo doesn’t seem to exist. In the end, I decided to go over 2 APIs:
While I’m not going to go to in depth, I want to walk through a few examples to give you the general idea of how a REST API works.
There are two main methods of authentication. One where the API is provided a username and password and the system gives back a temporary token. This process is often used in systems where you are running a system such as ACI. In the other method, a token you generate from a UI serves as your authentication. These are often used for web applications such as Slack, Twitter, Facebook, and fortunately NetBox, so we can see how to do that.
The general idea behind the REST API is you are going to an URL rather than typing in commands. Let’s look at this like a Cisco CLI. For example, your command might be show run interface gi1
. The equivalent URL might be something like device.com/api/conf/interface/gi1
so to work with gi2
the URL would just change device.com/api/conf/interface/gi2
.
The main types of calls are below.
GET
PUT/POST/PATCH
DELETE
With the same examples from above, we will do a GET
on device.com/api/conf/interface/gi1
. It returns the dictionary below:
{
description: "Disconnected"
status: "shut"
mode: "access"
vlans: 110
}
We now know the description on the interface, the VLAN, and that it is shut down. Let’s say we wanted to make a change, just update it as below and PUT
it to the same URL.
{
description: "VM Server Bob"
status: "no shut"
mode: "trunk"
vlans: "10-100"
}
The port is now up, with a new description, as a trunk allowing VLANs 10-100 though. While not quite as easy to understand the url above this URL https://device.com/restconf/data/Cisco-IOS-XE-native:native/interface=GigabitEthernet/1
would work for the RESTCONF API for IOS-XE devices and has an easy to read URL.
I am running an instance of NetBox in my lab, the IP is 192.168.0.159, so that’s what all my examples will be of. It’s not hardened or using https, so all of my examples are using http on port 8000. Your box may vary, adjust accordingly.
First, let’s build and grab a token from http://192.168.0.159:8000/user/api-tokens/
.
Next, we’ll look at the swagger documentation page at http://192.168.0.159:8000/api/docs/
. Login to the session
and find an action to do, (e.g. get a device from DCIM,) select that, then select “Try it out.”
We can pull all devices if we were to look at another option, but the API call gives us lots of options. A screenshot would be overly busy, so I am just going to pull one device in this example. Please note the required tag.
The API call gives us all the data for that we asked for. Please pay special note to the cURL script. That is the call it made to the NetBox API. It sent a GET
request to http://192.168.0.159:8000/api/dcim/devices/5/
please note the initial line we chose was /dcim/devices/{id}/
The cURL script is running an HTTP request to the URL listed, and you could run the same request on nearly all systems. For example, right now you can run “curl http://www.google.com” and make the HTTP call to Google. One of the really nice things about that cURL script is we can go to https://curl.trillworks.com, paste in the cURL script, and it will give us the Python equivalent. NOTE that if the request has the token in it you should be careful. Swap out the token before posting it to the website unless you like giving admin credentials to strangers.
Honestly there’s not much to be scared of here. Just working though Swagger and putting a few functions together from what you get from the website. Super easy… Now let’s look at ACI.
In this example, the Cisco DevNet ACI emulator will be used. Set the username, password, and the base URL. This URL will be in every call made. To compare to the NetBox example, the base_url would have been http://192.168.0.159:8000/api
.
username = "admin"
password = "ciscopsdt"
base_url = "https://sandboxapicdc.cisco.com/api/"
The login script is honestly one of the most difficult parts of the whole process, as documentation is often sparse and there is no industry standard for field names across REST APIs. The process involves simply logging in, getting a token, and returning it. This is how many APIs work, and there are generally small differences for every API. Most of this was from sample code obtained from DevNet a few years ago.
def get_token(username, password, base_url):
# Disable warning messages that come with using self signed certs
requests.packages.urllib3.disable_warnings()
# create credentials structure
name_pwd = {"aaaUser": {"attributes": {"name": username, "pwd": password}}}
json_credentials = json.dumps(name_pwd)
# Create the full URL we need in order to login
# You'll find we do this a lot
login_url = base_url + "aaaLogin.json"
# log in to API
# requests is a library that can do HTTP transactions, in this case we POSTing up (Telling it) our
# credentials to get the token back
# Verify=False ignores self signed certs, but would kick off errors if we hadn't disabled them up above
post_response = requests.post(login_url, data=json_credentials, verify=False)
# getting the token from login response structure
auth = json.loads(post_response.text)
login_attributes = auth["imdata"][0]["aaaLogin"]["attributes"]
auth_token = login_attributes["token"]
# create token dictionary that we will use for authentication from here on out for auth cookie
cookies = {}
cookies["APIC-Cookie"] = auth_token
return cookies
Let’s review the documentation for the ACI API. Once again, this is how it worked back in late 2018, the documentation may have changed since. Access the API inspector,
then proceed to the interesting area in the GUI, paying close attention to your last click. The example will return with the switch information, so I went to Fabric (top tap) -> Inventory -> POD1 (expanded). Sadly, the URL alone doesn't always give you exactly what you need. In this example I had to cut off the
&subscription=yes` section. Often, determinng the URL can be the hardest part.
You can also see that the URL is asking for JSON in the pod-1.json?
section. For ACI, it will return either XML or JSON. Personally, I find JSON easier to deal with in Python. Using the JSON library it’s not hard to convert JSON to a dictionary; https://www.w3schools.com/python/python_json.asp
does a better job at explaining the details. Let’s take a look at this process.
import json
import requests
from pprint import pprint
def pull_pod1(cookies, base_url):
sensor_url = (
base_url + '/node/mo/topology/pod-1.json?query-target=children&target-subtree-class=fabricNode&query-target-filter=and(not(wcard(fabricNode.dn,%22__ui_%22)),and(ne(fabricNode.role,"controller")))'
)
get_response = requests.get(sensor_url, cookies=cookies, verify=False)
return get_response.json()
username = "admin"
password = "ciscopsdt"
base_url = "https://sandboxapicdc.cisco.com/api/"
## Calling the authentication function above to get the auth cookie
cookies = get_token(username, password, base_url)
pprint(pull_pod1(cookies, base_url))
This gives us all the devices in POD1. I’ll just be putting in one device for the sake of space
{
"imdata": [
{
"fabricNode": {
"attributes": {
"adSt": "on",
"address": "10.0.112.64",
"annotation": "",
"apicType": "apic",
"childAction": "",
"delayedHeartbeat": "no",
"dn": "topology/pod-1/node-101",
"extMngdBy": "",
"fabricSt": "active",
"id": "101",
"lastStateModTs": "2019-12-12T14:22:14.238+00:00",
"lcOwn": "local",
"modTs": "2019-12-12T14:23:02.315+00:00",
"model": "N9K-C9396PX",
"monPolDn": "uni/fabric/monfab-default",
"name": "leaf-1",
"nameAlias": "",
"nodeType": "unspecified",
"role": "leaf",
"serial": "TEP-1-101",
"status": "",
"uid": "0",
"vendor": "Cisco Systems, Inc",
"version": ""
}
}
}
]
}
Note: Output omitted and modified for the sake of brevity and clarity.
You can observe several key pieces of information about this switch. The DN info, ID, model number, serial number, etc. With ACI, I often had to pull one piece of data so that I could understand how to pull another piece of data. Finding the DN was almost always an important step in my programs.
There is an easier, albeit less stable way (based on my observations) to find the URLs we want. Right-click on an interesting object and select “Open in Object Store Browser.” That will direct to a login screen. After login, the UI brings up a page where it should display the query and give the output.
To see the URL it used we click on “Show URL and response of last query.”
You can navigate the API structure and find what you are looking for. It may take some practice to develop a full understanding of this method.
I hope this has been helpful. I know it can be overwhelming, but the benefits of working with an API vs clicking though a web-page are wonderful. For example, before I left my last job I wrote a Python program that could be run on demand. It pulled what VLANS went to what VM Servers and looked for errors on interfaces. We got a call that there were lots of outages going on. The other network engineers were manually logging into everything looking for issues, but I just ran my program and was able to find a missing VLAN on a trunk to a VM server. The VM failed because it could no longer communicate out-side of the VM server. It took approximately five minutes to find and fix the issue. With dozens of VM servers and very little info on what the actual issue was, I believe that outage would have been much longer if people were looking for the issue by hand.
-Daniel
Share details about yourself & someone from our team will reach out to you ASAP!