Coding 101

# Check here for code samples as you work through this lab

How to call REST APIs from Python

As part of the DevNet Zone sessions at Cisco Live US 2014, the DevNet team presented a Coding 101 session to help new developers, returning developers and NetOps engineers who are beginning to work with REST APIs.

This Coding 101 session explains the basics of consuming a REST API, and walks you through making your first REST API calls from Python. You can also watch the video of the full session presentation.

In the Cisco Live session, we also introduced the NeXt UI Toolkit. We are going to cover NeXt in a separate learning lab.

Sample code disclaimer

The examples and code in this presentation are for learning and educational purposes.

The samples were created with the goals of clarity and ease of understanding. If you are writing code for a real application, you would write the code in a more efficient and structured style.

Before you begin

You will need to download and setup a few items before you can begin coding along with us.

So what is a REST web service?

What is a Web Service?

A web service is a way for two systems to communicate through a defined interface.
There are two major types of Web Services Ð REST or SOAP.

What is a REST Web Service?

REST is an architecture style for designing networked applications.
A REST web service is a web service that is as easy to call as making an HTTP request.
RESTful interfaces often offer the CRUD (Create, Update, Delete) operations.
If you want to know more about REST in general, this is a great REST tutorial.

What is so great about REST?

REST is easy to use on any platform

For this lab, we are going to use the pre-beta version of the soon to be released APIC-EM APIs.

Using the the APIC-EM APIs, we can retrieve information about devices on our network including a list of Hosts, a list of Devices, a list of Policies and a list of configured Applications. We are going to use these features as examples to learn how to make REST calls from Python.

REST in Action: Now let’s try it!

APIC-EM Example: Get Hosts

We need to know how to construct the request to retrieve the list of Hosts. So we need to use the API docs to determine the:

Method

URL

Headers

Authentication

Body

All of the urls for API-EM are in reference to:

http://server:8081/api/v0

To view the details of the Get Hosts endpoints from the APIC-EM API Docs open the api reference, and select Hosts in the left hand column, then choose /host.

GET /host/: returns information about all the hosts stored in the controller.

So that means that we will use the GET method, and the URL we will need to call to retreive a list of all of the hosts is:

http://server:8081/api/v0/host

We also need to know what kind of authentication to use.

APIC EM southbound interfaces that speak to individual network devices use the Cisco Command Line Interface (CLI),
authentication is done using the CLI user name and password for each of the devices. The JSON objects that are posted via the REST API have key-value pairs that are the user names and passwords to be applied.

So you will use a user name, password pair that is configured on your system.

So to get the list of Hosts, our request will be like this:

Method - GET

URL - http://server:8081/api/v0/host

substitute in address for the APIC-EM controller you are using

Headers

not required for this request

Authentication

Basic HTTP Authentication using credentials configured on your system

Body

not required for this request

Make a REST API Call

There are many HTTP clients that can help you quickly test web services.

Doing a POST or a PUT is slightly more complicated because you need to send data in the Request body, and you also need to send the Content-Type header to indicate what type of data you are sending in the body.

Let’s look at the policy endpoint as an example.
Open the api reference, and browse to the POST method for the /policy endpoint.

Python Examples

First REST call from Python

# Simple Example of calling REST API from Python# This is all that is required to call a REST API from pythonimportrequests# put the ip address or dns of your apic-em controller in this urlurl='http://YOUR-APIC-EMController/api/v0/host'# this statement performs a GET on the specified urlresponse=requests.get(url)# print the json that is returnedprintresponse.text

The code to the right makes a very simple GET request using the Requests library.

Let’s look at what the code is doing.

First, we import the requests library.

The next line creates a variable called url that contains the url we are going to use in the request.

response = requests.get(url) makes a request to the specified url using the GET method. The response from the endpoint is returned to the variable called response.

print response.text prints the json that was returned in the response to our screen.

That’s it. We made our first call to a REST endpoint from Python. Give it a try!

To run this code on your system:

Copy and paste the code in the right panel into a text file.

Insert your APIC-EM Controller IP address into the URL.

Save the file with the extension .py. For example, apic-em1.py.

Go to your command prompt, change to the directory where you saved the apic-em1.py file.

Type python apic-em1.py at the command prompt, and hit return.

The program should execute or display an error message.

If the program runs successfully, you will see the json that is returned displayed as text.

Next, we are going to build on the above simple example, and learn how to parse and use the json that is returned in the response.

apic-em-helloworld.py

# Getting started with APIC-EM APIs # First Call to APIC-EM REST API - "Hello World"# import the requests library so we can use it to make REST calls (http://docs.python-requests.org/en/latest/index.html)importrequests# All of our REST calls will use the url for the APIC EM Controller as the base URL# So lets define a variable for the controller IP or DNS so we don't have to keep typing itcontroller="http://YOUR-APIC-EMController/"# Get Devices# This function allows you to view a list of all the devices in the network(routers and switches).# Use our controller address plus the url for this API to construct the URL we need to callget_devices_url=controller+'api/v0/network-device'# Send Request using GET Method and specify URL# An object containing the response codes and json is returned.get_devices_response=requests.get(get_devices_url)# Print the response so we can see it.print"Devices = "printget_devices_response.text

learning-lab-create-policy.py Ð Shows how to create a new policy using the POST Method

The first example, apic-em-helloworld.py, uses the network-device endpoint to retrieve a list of network devices, and it is very similar to the Get Hosts example above.

In this example, we define a variable called controller to hold the base url for our endpoints.

We can then use this variable to build the endpoint url. This variable is handy when we are making many API calls because we don’t have to keep typing the base url.

Here an example of what you should see when you run apic-em-helloworld.py.

learning-lab-basics.py

# Getting started with APIC-EM APIs # Follows APIC-EM Basics Learning Lab# Hello World with JSON and pretty printing# import the requests library so we can use it to make REST calls (http://docs.python-requests.org/en/latest/index.html)importrequests# import the json library. This provides many handy features for formatting, displaying# and manipulating json. https://docs.python.org/2/library/json.htmlimportjson# All of our REST calls will use the url for the APIC EM Controller as the base URL# So lets define a variable for the controller IP or DNS so we don't have to keep typing itcontroller_url="http://YOUR-APIC-EMController/"# Get Devices# This function allows you to view a list of all the devices in the network(routers and switches).get_devices_url=controller_url+'api/v0/network-device'#Perform GET on get_devices_urlget_devices_response=requests.get(get_devices_url)# The json method of the response object returned by requests.get returns the request body in json formatget_devices_json=get_devices_response.json()# json.dumps serializes the json into a string and allows us to# print the response in a 'pretty' format with indentation etc.print"Devices = "printjson.dumps(get_devices_json,indent=4,separators=(',',': '))

In the previous examples, the json prints as one continuous text block that is really hard to read. Let’s pretty-print the json to make it more readable.

First, we need to import the json library. This library provides many useful methods for working with json.

Next, we make the API call as in the previous examples.

After, we get the response, we use the response.json() method to access the request body that is returned as a json object.

Then we use json.dumps() to print the json and specify the indentation that we want to use.

Here is an example of the output of learning-lab-basics.py. The JSON is much easier to read!

learning-lab-basics-step2.py

# Getting started with APIC-EM APIs # Follows APIC-EM Basics Learning Lab Step 2# Get Devices and Print networkDeviceId values# import the requests library so we can use it to make REST calls (http://docs.python-requests.org/en/latest/index.html)importrequests# import the json library. This library gives us many handy features for formatting, displaying # and manipulating json.importjson# All of our REST calls will use the url for the APIC EM Controller as the base URL# So lets define a variable for the controller IP or DNS so we don't have to keep typing itcontroller_url="http://YOUR-APIC-EMController/"# Get Devices# This function allows you to view a list of all the devices in the network(routers and switches).get_devices_url=controller_url+'api/v0/network-device'#Perform GET on get_devices_urlget_devices_response=requests.get(get_devices_url)# The json method of the response object returned by requests.get returns the request body in json formatget_devices_json=get_devices_response.json()#Now let's read and display some specific information from the json# set our parent as the top level response objectparent=get_devices_json["response"]print"Devices = "# for each device returned, print the networkDeviceIdforiteminparent:printitem["networkDeviceId"]

In the previous examples, we retrieved a list of the network devices and displayed the json that was returned.

However, just displaying the json is not very useful. We need to learn how to parse the json and use specific values from the json.

For this example, we will parse the json and print out the list of all of the network device IDs for the devices that are returned.

We make the call to get the list of network devices the same way that we have in the previous examples.

Then, we create an object called get_devices_json that contains the json object of the device information that was returned in the response.

Next, we create an object called parent and get a reference to the top level object named “response” in get_devices_json.

Now, we can loop through all of the child items of the parent object, and print out the “networkDeviceId” value for each item.

Here is what you will see when you run learning-lab-basics-step2.py.

This same pattern can be used to access any of the values in the json. Give it a try!

learning-lab-basics-step3.py uses this same kind of logic to retrieve the lists of devices, applications, hosts and policies.

learning-lab-create-policy.py

# Getting started with APIC-EM APIs # Follows APIC-EM Basics Learning Lab# Create a Policy Use Case# Basic Steps# 1. Get Hosts# 2. Get Policies# 3. Create Policy# 4. Get Policies again to show new one that was added# import the requests library so we can use it to make REST calls (http://docs.python-requests.org/en/latest/index.html)importrequests# import the json library. This library provides many handy features for formatting, displaying# and manipulating json.importjson# All of our REST calls will use the url for the APIC EM Controller as the base URL# So lets define a variable for the controller IP or DNS so we don't have to keep typing itcontroller_url="http://YOUR-APIC-EMController/"# Get Hosts# This function allows you to view a list of all the hosts in the network.get_hosts_url=controller_url+'api/v0/host'# Perform GET on get_hosts_urlr=(requests.get(get_hosts_url))hosts_obj=r.json()# For this example, we will use the IP Address of the second host in the listhosts_parent=hosts_obj["response"]selected_host=hosts_parent[2]["hostIp"]# Print the IP address of this host so we can see it.print"\nThis is the selected host = ",selected_host# Specify URL for policiespolicies_url=controller_url+'api/v0/policy'# Perform GET on policies_urlpolicies_response=requests.get(policies_url)# set our parent as the top level response objectpolicies_parent=policies_response.json()print"\nPolicies= "# Print list of policies before we add the new one# for each policy returned, print the policyIDforiteminpolicies_parent["response"]:printitem["policyId"]# print total number of policies before we create the new oneprint"Total number of policies before = ",len(policies_parent["response"])# Create a new Policy# We need to send JSON in the request to specify the attributes of our new policy.# The combination of policyName, hostIp, and the ports must be unique for a new policy.# This is the JSON we will use to# {# "actions": [# "DENY"# ],# "policyName": "blockport1234",# "policyOwner": "Admin",# "networkUser": {# "userIdentifiers": [# "20.0.0.3" # this is the HostIp# ],# "applications": [# "1234,1236,TCP"# ]# }# }# Create an object that holds our JSON to create a policy.# Use our selected_host variable that is defined above to specify the hostIPpayload={"actions":["DENY"],"policyName":"blockport1234","policyOwner":"Admin","networkUser":{"userIdentifiers":[selected_host],"applications":["1234,1519,TCP"]}}# To create a policy, we need to use the POST method.# When using POST, you need to specify the Content-Type header as application/jsonheaders={'content-type':'application/json'}# Use requests.post to do a POST to the policy API# Specify request body json data and headerspolicy_response=requests.post(policies_url,data=json.dumps(payload),headers=headers)print"\nResult of Create",policy_response.text# Display list of policies after our new additionget_policies_url=controller_url+'api/v0/policy'# Perform GET on policies_urlpolicies_response=requests.get(get_policies_url)# set our parent as the top level response objectpolicies_parent=policies_response.json()print"\nPolicies= "# print list of policies after the new policy was added.# for each policy returned, print the policyIDforiteminpolicies_parent["response"]:printitem["policyId"]# print total number of policies after we created the new policyprint"Total number of policies after = ",len(policies_parent["response"])

In this example, we will look at how to do a POST to create a policy.

We are going to use the ideas from the examples above to create a simple application that:

Gets a list of Hosts

Gets the IP address of the second host in the list

Gets as list of Policies and displays the current number of Policies

Creates a new Policy

Gets the list and count of Policies again to show that a new policy was added

1. Get a list of Hosts

The code for this is the same code we used in the previous examples to retrieve the list of Hosts.

2. Gets the IP address of the third host in the list

selected_host = hosts_parent[2]["hostIp"] creates a variable called selected_host and populates this variable with the hostIp value for the 3rd host in our list of hosts. In a “real application”, you would probably have the user select the host, but we are going to just use the 3rd one in the list to keep this example simple.

3. Gets a list of the policies and display the count of policies

First, we use the code from the previous examples, policies_response = requests.get(policies_url) to do a GET and retrieve the list of policies.
Then we parse the json, and print out the policeId of each policy.

for item in policies_parent["response"]:

print item["policyId"]

We also get the number of policies in the policy array and print that number.
len(policies_parent["response"])

To create a policy, we need to do a POST and send json in the request body that specifies the policy we want to create.

We create an object called payload that holds the json we are going to send in the body.

We also create a variable that we will use to specify the Content-Type header in our request.
headers = {'content-type': 'application/json'}

And finally, we pass the headers object and our payload object to the post method of requests to make the request.